XPlayer

Come fare integration test su un plugin per Jira 3.13.4

Pubblicato in Jira, tips da stoner il Mercoledì, 8 Luglio 2009

Quando si tratta di voler scrivere test di integrazione per il vostro meraviglioso plugin per Jira 3.13.4 (l’ultima versione di Jira, in attesa che la 4.0 esca dalla beta), ci si imbatte in una serie di problemi.
Dopo qualche indagine, sono riuscito a risolverli tutti, e mi accingo a condividere la soluzione adottata, nella speranza che possa servire a qualcun altro (anche a me stesso tra qualche mese…).

Premetto che stiamo sviluppato il plugin usando maven2, che per questo genere di cose è davvero molto comodo.
Se avete qualche dubbio, due riferimenti:

Il pom.xml generato da maven usando l’archetipo per plugin di Jira contiene una sezione “properties” che, all’inizio, si presenta così:

<properties>
    <atlassian.plugin.key>
       com.sourcesense.jira.plugin.MyWonderfulPluginToSaveTheWorld
    </atlassian.plugin.key>

    <!-- JIRA version -->
    <atlassian.product.version>3.13</atlassian.product.version>
    <!-- JIRA functional test library version -->
    <atlassian.product.test-lib.version>3.13</atlassian.product.test-lib.version>
    <!-- JIRA data version -->
    <atlassian.product.data.version>3.13</atlassian.product.data.version>
 </properties>

Qui trovate una descrizione di queste properties, assieme ai loro valori di default.

Ecco un estratto:

“atlassian.product.version” - version of the Atlassian product to compile and test against.

“atlassian.product.data.version” – version of the test resource bundle that contains the basic Atlassian product configuration data for the integration test environment. These versions mimic the actual Atlassian product versions. However we might only modify and release the relevant projects for the reasons of non-backwards compatibility of the new versions of Atlassian products. Therefore not every version of Atlassian products will have a corresponding version of the resource bundle.

La property “atlassian.product.test-lib.version” non è documentata, e per capire il suo significato dovete chiedere a Google, che vi rispondera’ con questa utile pagina.

“atlassian.product.test-lib.version” – The version of the testing library to use, as a general recommendation you should at least use version 2.0 or higher as it exposes more of the page’s content and provides quite a few extra helper classes to aid in your testing.

Benissimo, quindi io che sto facendo un plugin per la versione 3.13.4 di Jira, sostituisco questo valore nelle tre properties del POM

<properties>
 ...
    <atlassian.product.version>3.13.4</atlassian.product.version>
    <atlassian.product.test-lib.version>3.13.4</atlassian.product.test-lib.version>
    <atlassian.product.data.version>3.13.4</atlassian.product.data.version>
 </properties>

Detto, fatto.
Mi manca solo di creare il mio primo test di integrazione, rigorosamente nel package che inizia con “it”.

 package it.com.sourcesense.jira.plugin;

 import com.atlassian.jira.webtests.JIRAWebTest;

 public class JiraTest extends JIRAWebTest {

    public JiraTest(String name) {
      super(name);
    }

    public void setUp() {
      super.setUp();
      restoreDataWithLicense("JiraDataForTest.xml", ENTERPRISE_KEY);
   }

   public void testVerySimple() throws Exception {
      assertTextPresent("This JIRA site is for demonstration purposes only");
   }
 }

E copiare il dump esporato da Jira per avere qualche dato di test (JiraDataForTest.xml) nel folder src/test/xml/ del progetto del plugin.

A questo punto non mi resta che lanciare il seguente comando nella home della progetto

mvn integration-test

e aspettare con pazienza che maven scarichi quel Terabyte di jar di cui dichiara di aver bisogno.

Primo problema: la console di mvn mi dice

 [INFO] [jar:jar]
 [INFO] Building jar:
        /private/tmp/HelloWorldPlugin/target/MyWonderfulPluginToSaveTheWorld-1.0-SNAPSHOT.jar
 [INFO] [antrun:run {execution: generate-integration-test-config}]
 [INFO] Executing tasks
 [touch] Creating
  /private/tmp/MyWonderfulPluginToSaveTheWorld/target/test-classes/localtest.properties
 [propertyfile] Updating property file:
  /private/tmp/MyWonderfulPluginToSaveTheWorld/target/test-classes/localtest.properties
 [INFO] Executed tasks
 [INFO] [antrun:run {execution: pre-integration-test-user-ant-tasks}]
 [INFO] Executing tasks
 [INFO] Executed tasks
 [INFO] [atlassian-test-harness:start-fisheye {execution: start-fisheye}]
 [INFO] Skipping fisheye; startService is set to false
 [INFO] [atlassian-test-harness:start-confluence {execution: start-confluence}]
 [INFO] Skipping confluence; startService is set to false
 [INFO] [atlassian-test-harness:start-jira {execution: start-jira}]
 [INFO] Output log is set to /private/tmp/MyWonderfulPluginToSaveTheWorld/target/jira/output.log

E si blocca lì.
Vado a vedere il log segnalato nell’ultima riga della console, e scopro una pletora di eccezioni:

2009-07-07 16:11:19,568 main ERROR
[com.atlassian.license.LicenseManager] Exception getting license: java.lang.RuntimeException: contactLicense was null
 at org.picocontainer.defaults.DecoratingComponentAdapter.getComponentInstance(DecoratingComponentAdapter.java:42)
 at org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentInstance(SynchronizedComponentAdapter.java:35)
 ...

Indago, guardo su Google, niente.
Provo allora a sostituire 3.13.4 con 3.13.2 nelle tre properties del POM

<properties>
    ...
   <atlassian.product.version>3.13.2</atlassian.product.version>
   <atlassian.product.test-lib.version>3.13.2</atlassian.product.test-lib.version>
   <atlassian.product.data.version>3.13.2</atlassian.product.data.version>
</properties>

E rilancio “mvn integration-test”.
Stavolta l’errore è più chiaro: fallisce il ripristino del dump JiraDataForTest.xml nell’istanza di Jira 3.13.2 che viene avviata da maven, perchè la versione del dump è stata fatta con la 3.13.4, una versione successiva alla 3.13.2, e quindi Jira si rifiuta da caricarla. Eccheccavolo.

Vi risparmio tutte le combinazioni di numeri di versione che ho provato a mettere nel POM, senza successo, e vado dritto verso la soluzione.
Ecco il pom.xml che funziona

<properties>
 ...
 <atlassian.product.version>3.13.2</atlassian.product.version>
 <atlassian.product.test-lib.version>3.13.4</atlassian.product.test-lib.version>
 <atlassian.product.data.version>3.13.2</atlassian.product.data.version>
 </properties>

L’altra cosa da fare è modificare i dump di Jira che vorrete usare per i vostri test, in modo da far credere a Jira che sta importando una versione compatibile del dump.
Per fare questo dovete:

1. Aprire il dump xml di Jira che usate per i test (nel nostro caso JiraDataForTest.xml)

2. Cercare l’occorrenza di questa property

<OSPropertyEntry id="12345"
   entityName="jira.properties"
   entityId="1"
   propertyKey="jira.version.patched"
   type="5"/>

Per essere sicuri basta che cerchiate la parola “jira.version.patched”

3. Prendere nota dell’id di questa propery (es 12345) e cercare l’occorrenza di una OSPropertyString con lo stesso id

<OSPropertyString id="12345" value="354"/>

Ecco, quel valore (354) rappresenta la build version di Jira, che per la 3.13.4 è proprio 354.

4. Sostituire il valore 354 con 335, che è la build versione di Jira 3.13.2 e salvare l’xml

5. Rilanciare il test.

Tutto dovrebbe filare liscio ora…

 $ mvn integration-test
 ...
 ...
 [INFO] [jar:jar]
 [INFO] Building jar:
    /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/
    target/MyWonderfulPluginToSaveTheWorld-1.0-SNAPSHOT.jar
 [INFO] [antrun:run {execution: generate-integration-test-config}]
 [INFO] Executing tasks
 [propertyfile] Updating property file:
   /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/
   target/test-classes/localtest.properties
 [INFO] Executed tasks
 [INFO] [antrun:run {execution: pre-integration-test-user-ant-tasks}]
 [INFO] Executing tasks
 [INFO] Executed tasks
 [INFO] [atlassian-test-harness:start-fisheye {execution: start-fisheye}]
 [INFO] Skipping fisheye; startService is set to false
 [INFO] [atlassian-test-harness:start-confluence {execution: start-confluence}]
 [INFO] Skipping confluence; startService is set to false
 [INFO] [atlassian-test-harness:start-jira {execution: start-jira}]
 [INFO] Output log is set to
   /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/target/jira/output.log
 [INFO] Finished with jira goal
 [INFO] [atlassian-test-harness:start-bamboo {execution: start-bamboo}]
 [INFO] Skipping bamboo; startService is set to false
 [INFO] [surefire:test {execution: acceptance_tests}]
 [INFO] Surefire report directory:
   /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/target/surefire-reports

 -------------------------------------------------------
 T E S T S
 -------------------------------------------------------
 Running it.com.sourcesense.jira.plugin.JiraTest
 .
 . Started it.com.sourcesense.jira.plugin.JiraTest.test. Wed Jul 08 14:30:44 CEST 2009
 going to page secure/admin/XmlRestore!default.jspa
 Asserting text present: Your project has been successfully imported
 Asserting text present: This JIRA site is for demonstration purposes only
 .
 . Finished it.com.sourcesense.jira.plugin.JiraTest.test. Wed Jul 08 14:30:54 CEST 2009
 . The test ran in 10.542 seconds
 . The test suite has been running for 10.536 seconds
 . Max Mem : 66650112 Total Mem : 2727936 Free Mem : 268968
 . ______________________________
 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.045 sec

 Results :

 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

 [INFO] ------------------------------------------------------------------------
 [INFO] BUILD SUCCESSFUL
 [INFO] ------------------------------------------------------------------------
 [INFO] Total time: 44 seconds
 [INFO] Finished at: Wed Jul 08 14:30:55 CEST 2009
 [INFO] Final Memory: 32M/254M
 [INFO] ------------------------------------------------------------------------

Evviva, barra verde!!

Come generare il report HTML dei test eseguiti con Jmeter

Pubblicato in Jmeter, tips da stoner il Lunedì, 6 Luglio 2009

Problema: “Ho problemi con il report html dei test jmeter generato dalla trasformazione XSL indicata dalla documentazione di Jmeter (versione 2.3.4). Infatti nel report html si hanno due comportamenti anomali:

  • la prima riga è sempre raddoppiata, ovvero presente due volte (e vabbè, passi)
  • le colonne che indicano i tempi massimi e minimi non sono valorizzate (NaN)”

Soluzione: “Il problema è nella versione di Xalan inclusa di default nel JRE 1.5 e 1.6 di java. Si deve usare Xalan-J 2.4.1″

Descrizione più dettagliata:

Tutto è nato con dei test di carico e performance che stiamo eseguendo in questo periodo.
Jmeter, di cui usiamo la versione 2.3.4, esporta il report del test plan in formato XML (o CSV se specificato). Per convertirlo in un HTML più comodamente fruibile si deve effettuare una trasformazione XSL, e per fortuna Jmeter mette a disposizione degli stylesheet apposta per questo (si trovano nel folder /extras dell’installazione di Jmeter).

Detto, fatto. Il comando che eseguo dovrebbe essere (ad esempio)

java org.apache.xalan.xslt.Process
  -IN jmeterResults.xml
  -XSL ~/work/jakarta-jmeter-2.3.4/extras/jmeter-results-report_21.xsl
  -OUT jmeterResults.html

Purtroppo questa trasformazione non funziona, o meglio, produce un HTML incompleto (vedi sezione “Problema”).

Dopo diversi tentativi, ho scoperto la soluzione, grazie ad un commento a questo post: usando la versione 2.4.1 di Xalan-J l’HTML prodotto è completo.

Per la cronaca, per lanciare la trasformazione XSL a linea di comando specificando una propria versione di Xalan, basta fare così

java
  -cp xalan-2.4.1.jar org.apache.xalan.xslt.Process
  -IN log.jtl
  -XSL ../extras/jmeter-results-report_21.xsl
  -OUT foo.html



E in ant (cosa che interessava me in particolare, perchè lanciavamo jmeter da ant)

<xslt
  in="${scenario.target.path}/scenario-result.xml"
  out="${scenario.target.path}/AggregateReport.html"
  style="${jmeter.home}/extras/jmeter-results-report_21.xsl"
  classpath="${basedir}/../xalan-2.4.1.jar" />

Sul coaching…

Pubblicato in Agile da stoner il Lunedì, 23 Marzo 2009

Caspita, ne è passato di tempo dal mio ultimo post, e tante cose sono cambiate nel frattempo!
La cosa più importante che mi è capitata è che da oramai un anno ho felicemente cambiato lavoro, e grazie a Sourcesense ho avuto la possibilità di continuare a lavorare in un team agile, dopo la passata esperienza nel team XPlayers di Quinary, iniziata nel 2002.
E’ giusto giusto passato un anno da quando lavoro nel team Orione di Sourcesense, e, dopo diversi progetti di sviluppo e di mentoring, ho avuto l’occasione di fare il coach di una piccola parte del team.
Quest’ultima esperienza mi ha portato anche a fare una riflessione più generale sul coaching di un team agile, riflessioni che vorrei cercare di esprimere in senso più o meno compiuto qui di seguito.

teamwork!

E’ vero, verissimo, come spesso si dice e si sente dire, che ogni membro del team dovrebbe avere a cuore il processo e farsi carico di ricordare a tutti (per primo a sè stesso) le pratiche, i principi e i valori alla base del team stesso, soprattutto in condizioni di pressione o difficoltà. E’ quindi ragionevole dire che il team agile *maturo* è un team “senza coach”.
Eppure questa conclusione non mi soddisfa, e non mi convince del tutto.
Io penso che qualunque team, dal più “green” al piu’ navigato ed esperto, abbia bisogno comunque di un coach. Questo perchè ci sono alcuni “ruoli” che, sebbene possano essere interpretati da molti membri del team (da tutti i membri, in un team maturo), devono avere un interprete primario “designato” dal team.

Faccio un esempio: ognuno di noi sa che in caso di conflitti tra membri del team, o di atti di mancanza di rispetto, dovremmo tutti intervenire per riprendere la persona che ha “alzato i toni”, ma il coach, se presente, di certo interverrà per primo.
Stessa cosa se un customer o qualunque persona “esterna” al team di sviluppo cerca di “forzare” il team a lavorare in modi che contrastano con i valori o i principi alla base del team. In tal caso ognuno di noi, indistintamente, dovrebbe intervenire. Ma non si puo’ intervenire tutti, “in mucchio”. Ci vuole una persona che si faccia carico della cosa e che *per prima* protegga il team da interferenze e pressioni esterne, e per me quella persona è il coach.
So che magari a molti risulterà una metafora un pò lontana, ma pensiamo alle squadre sportive (per esempio di calcio). Lì esiste la figura del capitano, che spesso e volentieri non è neanche la persona più brava della squadra (ad es nel Milan(*) il capitano è Pirlo, non Kaka’ o Ronaldinhio), ma che certo interverrà per prima per riprendere un proprio giocatore se si comporta scorrettamente o se non segue gli schemi, o lo difenderà se è vittima di una decisione arbitrale giudicata ingiusta. Ancora, questo non vuol dire che gli altri giocatori non interverrano ugualmente in questi casi, ma di certo il capitano interverrà per primo e tutti i giocatori lo sanno, e quindi da questo punto di vista sono più “tranquilli”.

Per me questo è il ruolo primario e insostituibile del coach: è quello che c’è sempre e per primo. Se poi lui non può esserci, di certo gli altri faranno in modo che la sua assenza non si senta.

Tutto questo lungo e verboso discorso per dire che non penso basti chiedere che “ognuno di noi si prenda cura del processo”, anche se questo sforzo è condizione assolutamente necessaria al buon funzionamento del team.

P.S. Ricordo che nel team Xplayers in Quinary per un certo periodo il buon Piergiuliano Bossi (a cui devo moltissimo), come esercizio a margine degli studi teorici fatti sul libro bianco di XP di Beck, ci faceva, a turno, essere coach del team, per una iterazione. Questo ci serviva per capire che ruolo avesse davvero il coach.

(*) Nota: non sono milanista, è solo per fare un esempio.

Michael Feathers on TDD

Pubblicato in Mock Objects, Testing da stoner il Mercoledì, 18 Giugno 2008

Una breve riflessione di Michael Feathers sul TDD, passando per i mock objects per finire sulla necessita’ di adottare pratiche che “costringano” a ragionare e riflettere sul nostro codice.

Interessante anche l’excursus sulla storia dei mock objects, nati in Connextra

The story I heard was that it was all started by John Nolan, the CTO of a startup named Connextra. John Nolan, gave his developers a challenge: write OO code with no getters.  Whenever possible, tell another object to do something rather than ask.  In the process of doing this, they noticed that their code became supple and easy to change.

La frase chiave:

We need practices which help us achieve continuous discipline and a continuous state of reflection.  Clean Room and TDD are two practices which, despite their radical differences, force us to think with absolute precision about what we are doing.

Michael Feathers on testing private methods

Pubblicato in Testability, Testing da stoner il Venerdì, 8 Febbraio 2008

Da un articolo di InfoQ, la posizione di M.Feathers sul testare i metodi privati:

Michael Feathers suggested last year in The Deep Synergy Between Testability and Good Design that TDD encourages good design and, conversely, code that is not testable should make us think twice:

When I write tests and I have the urge to test a private method, I take it as a hint. The hint tells me that my class is encapsulating so much that it has ceased to be “understandable” by tests through its public interface. I listen to the hint, and factor my design differently. Usually, I end up moving the private method (and possibly some methods around it) to a new class where it can be non-private and accessible to tests.

Condivido al 100%!

E interessante anche quello che dice dopo, nel post originale, riguardo alla relazione tra coupling, cohesion e testabilita’.

In the end, it all comes down to cohesion and coupling.  If classes are deeply coupled with their neighbors, it is hard to control them in a test or observe them independently.  If a class isn’t cohesive, it may have some logic which is not easily exercisable through its public interface.

It seems that reverse is true also.  Classes which are hard to instantiate and use in a test harness are more coupled than they could be, and classes with private methods that you feel the urge to test, invariably have some sort of cohesion problem: they have more than one responsibility.

[Oracle Tips] Monitorare le connessioni aperte verso il db

Pubblicato in Uncategorized da stoner il Venerdì, 8 Febbraio 2008

Ogni volta che mi serve tenere d’occhio le connessioni verso un db Oracle mi ricordo vagamente della tabella Vqualcosa, ma il ricordo non e’ mai abbastanza nitido… Pertanto mi segno qui alcune query utili, una volta per tutte!

Per contare le connessioni aperte verso il db raggruppate per macchina client

select MACHINE, count(*) from V$SESSION group by MACHINE

Per contare solo quelle verso un certo schema

select MACHINE, count(*) from V$SESSION where schemaname = '<NOME DELLO SCHEMA>' group by MACHINE

Per contare solo quelle provenienti da certi client

select MACHINE, count(*) from V$SESSION where upper(machine) like '%<NOME DELLA MACCHINA CLIENT>%' group by MACHINE

Per contare le connessioni aperte verso il db raggruppate per utente

select osuser, count(*) from V$SESSION group by osuser;

Per contare tutte le connessioni aperte (vabbe’, questa e’ banale!)

select count(*) from V$SESSION;

Per vedere anche lo stato della connessione

select count(*), status from V$SESSION group by status;

Taggato con:, , ,

[a-ha! moment] Finalmente ho capito la configurazione di DBCP

Pubblicato in programming da stoner il Martedì, 5 Febbraio 2008

Questo post e’ piu’ che altro indirizzato a me stesso nel futuro, ma ovviamente se potesse servire ad altri, ora o nel futuro, ne saro’ contento.

Ho finalmente capito il significato dei parametri di configurazione di DBCP!
I miei dubbi riguardavano in particolare i parametri minIdle, maxIdle e maxActive.

Le connessioni aperte in un dato istante possono potenzialmente essere comprese tra zero e maxActive.

Quando il n. di connessioni aperte e’ compreso tra maxIdle e maxActive, tutte le connessioni ritornate al pool saranno immediatamente chiuse dal pool.

Quando il n. di connessioni aperte e’ compreso tra minIdle e maxIdle, tutte le connessioni ritornate al pool saranno soggette all’eventuale evictor (che si attiva con la prop timeBetweenEvictionRunsMillis). Questo significa che quando l’evictor parte, chiudera’ tutte le connessioni in eccedenza (rispetto a minIdle), ovviamente secondo le impostazioni dei parametri numTestsPerEvictionRun e minEvictableIdleTimeMillis (quest’ultima in particolare indica quando tempo una connessione ‘in eccesso’ puo’ rimanere idle nel pool prima di essere considerata ‘chiudibile’ dall’evictor thread).

Quando il n. di connessioni aperte e’ compreso tra zero e minIdle, tutte le connessioni ritornate al pool saranno lasciate nel pool. In altre parole non si dovrebbe mai scendere al di sotto di minIdle connessioni aperte verso il db.

Ora, magari questo puo’ sembrare scontato a voi, ma a me no! DBCP ha una documentazione piuttosto fumosa, e in particolare faccio ancora fatica a capire la differenza tra i vari parametri di configurazione… per esempio, cosa si intende per abandonedConnection? E come si distingue da una normale connessione idle?

Comunque intanto mi godo il mio a-ha! moment :)

Measuring Lines of Code

Pubblicato in Metrics, Productivity da stoner il Giovedì, 17 Gennaio 2008

Mi e’ piaciuta molto questa frase di Kent Beck da una mail sulla lista xp americana, sull’uso del numero di linee di codice come metrica di produttivita’:

Lines of code has many limitations — it’s like measuring a factory based on its consumption of raw materials not on its output.

Ricorda un po’ quella di Edsger W.Dijkstra

Yet people talk about programming as if it were a production process and measure “programmer productivity” in terms of “number of lines of code produced”. In so doing they book that number on the wrong side of the ledger: we should always refer to “the number of lines of code spent”.

Taggato con:,

Il mio feedback sull’agile day 2007

Pubblicato in AgileDay da stoner il Lunedì, 26 Novembre 2007

Complimenti a Marco e agli altri organizzatori, l’agile day 2007 e’ stato molto interessante, con contenuti ed organizzazione migliori dello scorso anno.

Cosa mi e’ piaciuto:

  • lo speak di Tim Mackinnon, uno dei padri dei mock objects. In particolare:
    • l’accento sull’appreciative inquiry e sul positive thinking
    • le pratiche da lui adottate, in particolare quelle a supporto della riflessivita’ e del miglioramento continuo del team, come la timeline e la retrospective
  • l’esperienza di Antonio Terreno su un progetto in ThoughtWorks dove c’era stato un cambio totale di team, e si e’ riusciti a fare bene
  • le incursioni di Francesco Cirillo, vedi la campagna anti-if (comprate la maglietta anti-if!) e le riflessioni su cosa voglia dire essere agili e su quali passi dovrebbe intraprendere la comunita’ xp italiana per favorire la diffusione dei metodi agili.
  • la presentazione di Alessandro Ruzzon sull’uso di Spring in progetti “agili” (!!)
  • in generale ho apprezzato la logistica a supporto delle presentazioni, vero punto debole dell’agile day 2006 (troppo rumore perche’ non c’era una vera separazione delle diverse track “concorrenti”)

Cosa non mi e’ piaciuto:

  • la location (troppo difficile da raggiungere)

Ah, e ovviamente, aderite anche voi alla campagna anti-if! Campagna Anti-IF

Microsoft e il continuous improvement

Pubblicato in Agile, ContinuousImprovement da stoner il Lunedì, 26 Novembre 2007

Su InfoQ si parla di tal Jay Bazuzi, “Development Lead for the C# Editor“, che lascia Microsoft e che in occasione di questo “evento”, ha postato sul suo blog delle riflessioni su alcuni punti deboli nello sviluppo del sw in Microsoft.

sunset in val di casies
A parte le osservazioni su come in Microsoft si usa l’OO o si applica refactoring, l’ultimo punto e’ particolarmente interessante, quando parla di “doing better”.

Le domande che Jay raccomanda di farsi per migliorare (e che lui personalmente si faceva e faceva al suo team) sono

  • “How can I make sure this problem goes away forever?”
  • “How can I produce fewer bugs?”
  • “How can I make it easier to fix the bugs I have?”
  • “How can I make it easier to respond to change quickly?”
  • “How can I make it easier to make my software fast enough?”

Mica paglia!

Che sotto sotto Jay volesse rendere piu’ agile Microsoft? :-)