20081118: Agile Open France 2009

Ça y'est, c'est officiel, les inscriptions à la conférence Agile Open France 2009 sont désormais ouvertes. Bien évidemment, je me suis précipité sur le formulaire d'inscription dés réception de l'annonce. J'ai eu la chance de participer à l'édition 2008 et je dois dire que j'en suis revenu enthousiasmé par le format Forum Ouvert (ou Open Space) qui, à mon sens, est la meilleure manière de produire un tel événement.  

J'aurais voulu utiliser ce format pour Agile Tour Lille 2008 mais celui-ci suppose que les participants aient déjà une idée de ce qu'ils viennent trouver et apporter, ce qui n'était pas nécessairement le cas du public de l'Agile Tour.  

La deuxième journée des Valtech Days était aussi organisée selon ce modèle et je dois dire que c'est la partie que j'ai le plus appréciée.  

J'attends avec impatience le mois de janvier pour être de nouveau surpris.  

20081114: Paying back technical debt

I am struggling to improve my Muse parsing software, a project that I have been working on for couple of years now and which I use to publish this blog and the whole OQube's site. It is based on the Emacs Muse wiki format which means that I can operate within the context of Emacs with all the useful tools and keyboard shortcuts I find there, and be able to serve muse content over the web in various backend formats, HTML and RSS feeds being the most useful.

As all software, especially software that is used and maintained by a single person, it is made of layers of code injected haphazardly from urgent needs or nigthly ideas.  

There are some basic flaws in the design of the key elements of this software that are now impediments for further evolutions:  

  • the parser is one giant class with a state-machine coded usingswitches and state variables. The basic idea was to provide someway to customize it through a simple chain of responsibilitypattern, but it is now so large that any modification would bedifficult,
  • the sink interface is not open, it is modelled after the XMLContentHandler interface from SAX which means events are nothandled uniformly,
  • composability is very adhoc. I would like to be able to composeevent stream handlers much like an algebraic structure, withfilters and transformers, like what you can do in functionallanguages.

This means that the time spent implementing new features is raising. I am trying to implement a simple slide formatter, something that I could use to produces slide-shows for talks, including code samples for online execution and examples in Javascript, something along the line of what John Resig published on his site

To implement this, I need one of the following but both are hards to implement using the current state of my muse parser:

I have already had 3 false starts trying to implement this, which is a sure sign that something gets wrong: either me or the software is becoming very reluctant to change.  

20081113: Dojo de Lille

Aujourd'hui au dojo, nous avons fait en randori du refactoring d'un bout de code Java (voir cette page). Nous avons eu le plaisir d'accueillir de nouveaux venus dont certains que je connaissais par ailleurs (Michael et Vincent, que j'ai rencontré dans le groupe PYLULE), d'autres que je ne connaissais pas. Il semblerait que l'Agile Tour ait porté ses fruits et intéressé des gens à la pratique de l'agilité.  

Le code de départ est le suivant:


 /**
   * Split a signature string into its constituent parts and fills the list
   * argument.
   * 
   * @param sig
   *          java standard signature
   * @param ret
   *          a List where parsed arguments will be stored. May be null.
   * @return a List<String> of arguments' types plus return type. The strings
   *         correspond to the standard internal encoding of the JVM.
   */
  public static List parseSignature(String sig, List ret) {
    if (ret == null)
      ret = new ArrayList();
    int i = 0;
    int j = 0;
    int ln = sig.length();
    StringBuffer arr = new StringBuffer();
    parse: while (< ln) {
      switch (sig.charAt(i)) {
      case '(':
        i++;
        break;
      case 'L':
        j = sig.indexOf(';', i);
        arr.append(sig.substring(i, j + 1));
        ret.add(arr.toString());
        arr.delete(0, arr.length());
        i = j + 1;
        break;
      case ')':
        i++;
        break;
      case '[':
        i++;
        arr.append("[");
        break;
      default:
        arr.append(sig.charAt(i));
        ret.add(arr.toString());
        arr.delete(0, arr.length());
        i++;
      }
    }
    return ret;
  }

Nous n'avons pas achevé le remaniement du code, l'objectif initialement fixé étant de restructer le code pour le rendre plus lisible et maintenable tout en gardant le comportement identique. La motivation est d'envisager une évolution future de code, par exemple une modification de signature pour rendre explicite des conditions d'exception, ou une suppression de paramètres du fait du non-respect du Principe de Responsabilité Unique

La stratégie adoptée a consisté:  

  1. à fixer le comportement global de la fonction dans des tests "derecette" à partir des informations contenues dans la Javadoc,c'est—à-dire de rendre la Javadoc exécutable
  2. à explorer certains comportements non prévus, comme par exemple lecomportement d'une signature nulle (rien n'en est dit dans la doc,mais le cas est prévu pour le second paramètre donc on peutsupposer que l'auteur du code a envisagé la question et qu'on nes'attend pas à avoir une signature nulle). Dans ce dernier cas, onva rendre explicite le comportement dans le code à l'aide d'uneexcepition qui pour l'instant sera NullPointerException mais quipourra évoluer par la suite;
  3. une fois le comportement "bordé", on peut s'attaquer au corps dela méthode, d'abord en renommant correctement les variables;
  4. puis en détaillant par des tests les différents cas prévus parl'instruction switch interne, ce qui nous permettra d'extraire cesdifférents cas particuliers dans des fonctions/objets connexes etdonc de simplifier le code.

Dans le cas de cette fonction, il est clair que l'on est en présence d'un parseur donc que l'utilisation d'expressions régulières ou d'autres mécanismes d'analyse syntaxique serait pertinents et rendrait le code beaucoup plus explicite.  

Aucune vérification de correction n'est faite sur les constituants de la signature, par exemple sur le nom d'une signature représentant une classe ou sur les différents codes possibles pour les types primitifs. Cette fonctionnalité pourrait être facilement ajoutée une fois l'analyse syntaxique correctement réalisée.  

20081110: Type layering pattern

This is an interesting idiom found in the paper on reactive programming in Haskell, that constructs new types using layers of type constructors, each layer providing additional properties through instances of type classes:

It is used to define an improved time type with some useful properties to work on timed-streams:

previous
type FTime = Max (AddBounds (Improving Time))
newtype Max a = Max a deriving (Eq, Ord , Bounded )
instance (Ord a, Bounded a) => Monoid (Max a) where
  mempty              = Max minBound
  Max a `mplus` Max b = Max (a `max` b)


data AddBounds a =
  MinBound | NoBound a | MaxBound deriving Eq


instance Bounded (AddBounds a) where
  minBound = MinBound
  maxBound = MaxBound


-- An improving value. Invariant:
-- compare I iv compare (exact iv )
data Improving a =  Imp{exact :: a, compare I :: a -> Ordering }


compare :: Ord a => Improving a -> a -> Ordering
exactly :: Ord a => a -> Improving a
exactly a = Imp a (compare a)


20081109: Functional Programming IS hype

20081022: Valtech Days - Day 2

Dojo refactoring legacy code

Dojo randori sudoku

  • faire un plan ! think
  • exposer les problèmes potentiels, les trous des exigences (contratsou comportements attendus) le plus tôt possible

Enterprise Agile (Jeff McKenna)

  • lot of low-level things, interested in what's going on at theenterprise level, agile adoption

list of topics:

  • could we avoid involving the client ? Old-school clients, PM actingas the client surrogate (ie. Product Owner)
  • agile team within non-agile organisation
  • role of QA in agile
  • how to become an agile enterprise
  • can the whole organization be agile
  • what is an agile enterprise
  • convincing people to switch to agile process ? (eg. switching partof the team, other part against agile and how should we convincethem)
  • timeframe for transitioning
  • steps for adoption to agile
  • building up on the success of a pilot project
  • how to stay agile
  • agility: now what ?

voting: -> Arguments to convincing

  • usual voting scheme is to vote for and against

Arguments for convincing

What's worked ?

  • trust me, expert point of view
  • see the results quickly, readjusting quickly
  • first, acknowledge pain and unexpensive experiment
  • identifying a pilot (implies people are already convinced)
  • demonstrate: just do it ! make the metrics visible
  • bring in the experts

  • you cannot convince someone if they don't want to be convinced
  • you need to acknowledge pain
  • the more you are pushing your arguments, the more you findresistance
  • fear, change
  • interview each person at start of project, require agreement fromthe people (10-20% don't want to do it)
  • problem is at middle-management level, jobs more threatened thanthe "troops", need commitment from management
  • useful for convincing management is detailing each XP practices andits advantages
    • CI allows bug detection early
    • Collective Code ownership allows any people to work on any partof the code
  • Resistance as a resource, Dale Emery: listening to resistance,extracting information, something will happen

cf Guitart, "l'envie de s'y mettre"

Agile team inside non-agile organization

  • organization as to willing to look at itself, being conscious,learning organization, retrospective is a tool to lear
  • start measuring something and show the data: ratio of working toadd feature vs. working to fix bug
  • organizations need to be conscious of what's going on
  • managers are starting to lose control on people, they aredisturbed, losing/keeping control
  • about changing organization, not agile

Programming languages

Paul Graham in Hackers and Painters: "Language matters, and LISP is the best one because of macros/meta-programming and its sheer rarity."

  • question
    • what language do you use daily
    • what languages do you know
    • what language do you prefer
  • languages categories: scripts, compiled, interpreted,strongly-typed, dynamic typing, functional, object-oriented, ...
  • why do you prefer that language

Programmation Postmoderne

  • modernisme et pré-modernisme: Hegel, Kant, le sens de l'histoire
  • narratives: justification par le futur, ce qui doit/peut arriver,grandes histoires, valeurs fortes

20081021: Session sur les langages

  • La question du langage de programmation est intéressante etdifficile. Tout le monde s'accorde pour dire que certains langagessont mieux que d'autres, généralement en restreignant le meilleur àun champ déterminé pour lequel ce langage s'est révéléparticulièrement adéquat (ou a conquis une part de marchéconséquente). Néanmoins, quand il s'agit de rentrer dans le détailet:
    1. de classer les langages, et
    2. de définir des critères la plus largement possible acceptéspour produire ce classement,rien ne va plus
  • L'absence de consensus signifie que chacun tient à son langagepréféré, pour des raisons diverses, et que cette appropriationrelève de l'acte de foi, de la croyance
  • la foi est le produit d'une révélation ou d'une pratique, et bienplus souvent des deux: "Priez et vous croirez !" plutôt que "Croyezet vous prierez !"
  • de même qu'il est impossible de classer les religions en fonctionde la foi, il est impossible de classer les langages en fonctiondes programmes qu'ils permettent d'écrire
  • les seuls critères de classement possibles sont nécessairementexternes: nombre de développeurs, volume de code produitglobalement, nombre de projets "échoués" avec ce langage, nombre deprojets "réussis", nombre d'offres d'emplois...
  • ces indicateurs externes, d'ordre statistique, sont le produit decomportements individuels qui eux sont produits par des croyances,soit directes, soit indirectes
  • on a donc un système auto-référentiel: un langage est meilleurqu'un autre parce que les gens pensent qu'il est meilleur, et lesgens pensent qu'il est meilleur parce que d'autres personnes lepensent
  • cette situation masque la vérité de tout classement des langages,qui est que celui-ci est nécessairement contextuel: certainslangages sont meilleurs que d'autres localement (selon telle ettelle dimension), mais jamais globalement
  • la "valeur" d'un langage peut ainsi être vue comme la "valeur" d'unactif sur un marché (assez peu volatile)
  • cette valeur est déterminée par une relation  de proportion entreun risque (en l'occurence, le risque de ne pas satisfaire lesutilisateurs) et une rentabilité (la rapidité avec laquellel'application sera développée, le nombre d'utilisateurs,...)
  • la valeur d'un "systéme" est alors dans ce modèle la valeur d'un"portefeuille" d'éléments du système
  • l'absence de liquidité du marché limite considérablement le modèle,probablement au niveau d'une métaphore: on ne peut pasinstantanément échanger des applications comme on échange desactifs financiers
  • néanmoins, ce modèle peut être utile et est utilisé comme outil dedécision

20081021: Valtech Days - première journée

En vrac

  • pas mal de monde, dont quelques suspects usuels retrouvés avecplaisir
  • une rapide discussion avec Sadek à propos d'Haskell, il faudrapoursuivre demain si le temps nous le permet
  • j'ai déjà proposé deux sujets pour l'OpenSpace de demain, un sur laProgrammation fonctionnelle et un autre sur les langages deprogrammation

Programmation concurrente

Session présentée par Julien Delhomme, consultant chez Valtech, sur les différents modèles de concurrence et l'avenir de ce type de programmation pour le commun des mortels. Plusieurs modèles ont été présentés:

  1. processus multiples et mémoire partagée (eg. Java), le modèlestandard, le plus bas niveau et le plus compliqué à programmer
  2. vectorisation du code (eg. OpenMP), plutôt que parallélisation. Onproduit des calculs concurrents isolés
  3. mémoire transactionnelle (Software Transactional Memory,eg. Haskell)
  4. passage de messages (eg. Erlang)

  • quels outils pour aider à la programmation concurrente ?

Dojo - Kata Mastermind en Ruby

Kata présenté par Emmanuel Gaillot et Etienne Charignon, sur le théme du Mastermind. Excellente préparation, avec un bon déroulé du problème et un gros effort d'explication de la part du copilote et du pilote. La solution obtenue est effectivement assez élégante, encore que l'on aurait pût - dût ? - remanier le code plus tôt de manière à introduire les abstractions objets plus facilement (eg. le comptage des jetons bien et mal placés).

Questions intéressantes sur:

  • l'apport du dojo pour ses participants: acquisition de réflexes,découverte de nouveaux langages, habitude de la programmationcollective et en public (sous le regard d'autrui),
  • le degré de tolérance pour le remaniement,
  • l'applicabilité à des problèmes concrets d'entreprise.

20081021: Web application - Part 4 - Thinking

I have (nearly) the skeleton of a working web application in Haskell, using FastCGI interface, storing session in a map keyed by some integer value, and allowing users to login, register, logout... The UI is pure HTML with javascript based on jQuery and its rich API: data is requested from the server that is supposed to return javascript or JSON objects, which will be used by the client side to update the UI. This last part is still missing, but I am feeling I have reached, once again, some dead end.

The structure of the application is embedded within the services it exposed, meaning that, for example, that register updates the session to allow login action, which in turns update the session to allow logout  and unregister, etc. As those functions work on with a database, this implies that to both work with I/O and with the state of the application, they must work within a StateT monad transformer parameterized with IO monad. We can foresee that composing additional behavior will entail more and more monad stacking for eg. logging, error tracking.

What we really is, as usual, decouple the various components and layers we are working with so that each function serves one and only one purpose and that the top-level application orchestrates them. At this stage, without thinking about additional monadic behaviours, we would like to distinguish at least two layers:

  • the functional layer exposing stateless services as functions,which in turn may be of two kind:
    • IO functions,
    • and pure functions,
  • the applicative layer that orchestrates the availability offunctions according to some stateful computation, representing asusual the state of the interaction between the client andserver.

Being a very flexible language, Haskell should allow us to express composition of fucntions and behaviours externally, without the individual elements ever noticing they are part of some composite application. What is done with XML configuration files in Spring-like frameworks should be done in Haskell within the Haskell framework.

This idea I have already implemented in Java while developing the M-Commerce framework, which used an underlying finite-state machine to orchestrate access to service and the Pico container framework for dependency injection. DI within Haskell can be done:

  • at the module (linking time) level, by importing/exporting theright modules according to the configuration we want,
  • at the function level, by composing needed functions withcompatible types,
  • at the type level, by constructing new types from existing typesaccording to the needs.

20081020: Dojo Paris

Sujet

  • kata RoR, présentation du framework

  • scaffolding: initialisation des différents composants du systéme,génération des active records
  • notion  de migrations, scripts d'initialisation/terminaison de labase de données, exécuté par rake journalisé et versionné

20081020: Pattern-matching with functions only

While researching a way to represent pattern-matching easily within the My Language is Bigger than Yours boardgame, I came upon a book chapter on google-books that precisely defines a program transformation for eliminating pattern-matching and recursive data-types definitions from Haskell progras, thus making them composed only of functions and primitive constants.

The key to this transformation is to observe that any datatype can be equivalently transformed, using what is called Church-encoding into a set of constructor and desctructor functions. This I have already shown in a preceding article (see this article ). Given the following data-type for binary trees:

previous
data Tree a = Leaf a
            | Node a (Tree a) (Tree a)

we can define the constructor functions:

previous
type Tree a = ((a -> c) -> (a -> t -> t -> c) -> c)
Leaf a      = (\l n -> l a)
Node a t t' = (\l n -> n a t t')

If we want to know the value stored at a tree, we can either write, using pattern-matching:

previous
value Node x     = x
value Leaf x _ _ = x
or, using constructor functions:
previous
value t = t (\a -> a) (\a  _ _ -> a)

The key insight to this transformation is to notice that:

  1. each term of the sum of an algebraic datatype is basically a functionthat produces a type given some arguments, and
  2. an algebraic data type can be viewed as a selector function thatdiscrimates between various possible behaviours according to itsinternal structure.

So we can hopefully generalize this transformation to any datatype: given a type defined as a sum of products as:

previous
data Type = Ctor1 p11 p12 ... p1n
          | Ctor2 p21 ... p2m
          | ...
          | Ctork pk1 ... pkl

we can define the type Type and a set of constructor functions ctor1, ..., ctorl defined as:

previous
type Type = (t11 -> ... -> t1n -> c)
         -> (t21 -> ... -> t2m -> c)
         -> ...
         -> (tk1 -> ... -> tkl -> c)
         -> c


ctor1 v11 ... v1n  = (\ f1 f2 .... fk -> f1 v11 ... v1n)
ctor2 v21 ... v2m  = (\ f1 f2 .... fk -> f2 v21 ... v2m)
...
ctork vk1 ... vkl  = (\ f1 f2 .... fk -> f2 vk1 ... vkl)
and then each function operating on Type that wants to pattern-match against its instances should be implemented by passing the specialized function to the given instance of Type that it is passed.

This can be generalized to n-ary pattern matching in the obvious way, ie. by currying.

20081018: Filtering sections from Muse RSS Feed

It took me quite a few hours to implement a feature that, at first sight, appears quite simple: Extract and display a single entry from a log file as an HTML page. This feature is meant to be used as the target of links from RSS feeds. Given the unexpected difficulty of the task, and the somewhat contorted solution I found, I felt it would be worthwhile to provide details of the problem, the various paths I took and the (partial) solution I found.

The context

I am developing a set of Java components based on the Emacs Muse publishing format, a simple wiki-like text format that I discovered a few years ago while using EmacsPlanner tool. Among other things, this blog and the whole OQube's site are written in Emacs Muse and published on the web using a dedicated web application.

This system is composed of, among others, the following components:

  • a parser, this is of course the core element of the Muse-in-javasystem. The input is analyzed and transformed into calls to aMuseSink instance, an interface somewhat like the SAXContentHandler interface (actually, among other improvements, I amplanning to replace this specialized interface with true SAXinterfaces),
  • various specialized backends, ie. implementations of the MuseSinkinterface, that produces other formats: XHTML, Trac wiki syntax,RSS 2.0 feeds and a copycat Muse format producer,
  • specialized publishers for each format, that handle thenitty-gritty details: reading files, setting input and outputencoding, headers and footers definition...
  • a web application based on simple servlets that publishes one ormore directories containing muse files as a web site in HTML andRSS formats, together with associated image and other files.

The problem

The Feed backend produces a RSS 2.0 formatted news feed from a muse file: Each level one title is considered a news item if the title contains a date formatted as YYYYMMDD. In the RSS 2.0 format, each entry is identified with a supposedly unique URI, which is formed by transforming the date to YYYY/MM/DD format and suffixing it with the title from which all non-ascii characters have been escaped (I should use the standard urlEscape() function).

For example, this news item is transformed to:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>OQube's Blog</title>
    <link />
    <description>Random things I am working on...</description>
    <dc:creator>Arnaud Bailly</dc:creator>
    <item>
      <title>Filtering sections from Muse RSS Feed</title>
      <description>20081018: Filtering sections from Muse RSS Feed 
&lt;h2&gt;The context &lt;/h2&gt;
&lt;p&gt;I am developing a set of &lt;em&gt;Java&lt;/em&gt; 
      <pubDate>Fri, 17 Oct 2008 22:00:00 GMT</pubDate>
      <guid isPermaLink="false">http://localhost:4444/journal/journal/2008/10/18/Filtering-sections-from-Muse-RSS-Feed</guid>
      <dc:date>2008-10-17T22:00:00Z</dc:date>
    </item>
...

The problem lies in the <guid> tag: Using the given URI in a browser should provide the given news in XHTML format. The news item should be extracted from the muse file which contains all the news. The question is: how to extract the selected news (or any other subset of the file) from the muse file and format it ?

Abstracting away from the details, this problem can be narrowed to the following: Given a stream of objects, how to select a subset of this stream using simple rules ? For example, if we consider a stream of a-z letters, we want to select the substream that is contained between the letters ab and the letters yz. Applying this rule to the word jlkjlkabcdeffdsfyzflsfdkj, we obtain the word abcdeffdsfyz

It is obvious that, in the case of letters, this problem boils down to regular languages matching: extract some rational pattern from a stream (ie. a word). But in the case of events, we are facing a somewhat different problem: the alphabet is infinite which implies the language is not regular. Furthermore, writing a  finite state machine ie. a regular language acceptor, while not an extremely complex task, is not immediate and quite error-prone.

The (path to the) solution

The idea I explored is based on event filtering and the Decorator pattern:  

  • a MuseSink instance wraps the target sink and is set as backend forthe publisher for HTML,
  • it passes SinkEvent instances, to
  • its SinkFilter instance filter() method which returns whether ornot the event should be filtered,
  • if the answer is true, then the wrapper sink passes the event tothe real sink.

The problem I faced was managing the state of the underlying FSA so that parsing events can be passed in a timely fashion:

  • if a filter starts with a sequence of events, then it needs tobuffer the accumulated events until they match the requiredpattern,
  • but this implies that the sink:
    • either knows when the pattern match, and then knows how toextract the accumulated events to be passed to the sink,
    • or delegate the passing of events to the filter.

Neither of this solutions is satisfying:

  • the first one is too complex and breaks the Single ResponsibilityPrinciple and encapsulation by making the sink dependent on theprecise implementation of the filter,
  • the second one also breaks the SRP and produces filters noncomposable

The choosen solution

The idea is to define the filter as a SinkEvent transformer, a function from SinkEvent to SinkEvent that returns the event to be passed to the real sink. A special event is defined, NullEvent that is a non-event and, when asked to activate a sink, does nothing.  By defining filters as transformers, they can be composed.  

previous
public interface SinkFilter {
  SinkEvent filter(SinkEvent e);
}

Filters are then made into combinators that operate on stream of sink events and, together with filter operators, can form a whole algebra of stream transformers and selectors.

For this to works, I also needed to create a CompoundEvent object that implements the SinkEvent interface and that passes all accumulated events to its underlying sink. This is needed because, when the filter needs more than one event lookahead, it buffers the events passed so far and it cannot produce the correct event before having matched the whole pattern.  

previous
public interface SinkEvent {
  SinkEvent NULL_EVENT = new SinkEvent() {...};


  void passTo(MuseSink sink);
}


The interesting thing about a combinator-based implementation is that, being in essence purely functional, it has a number of interesting properties:

The current implementation suffers however from some defects and is not fully functional

Actually, one would like filters to be much like a boolean algebra, closed under the usual operators: negation, intersection, union, difference, much like lists are in most functional languages, and like rational languages are.

20081015: Agile Tour 2008

Ça y'est, l'Agile Tour 2008 est passé par Lille ! En voici un bref résumé, plus quelques idées pour une prochaine manifestation.  

Les sessions

Nous avons eu pas mal de désistements, mais il faut dire que nous n'avons vraiment pas assuré côté relance: un mail tardif et une relance téléphonique la veille pour le lendemain. Seuls les gens que François et moi connaissions personnellement sont finalement venus.  

Les sessions proposées étaient donc les suivantes:

  1. Introduction à l'agilité, par François Wauquier
  2. Les projets Agile - de la réponse aux appels d'offres à lamaintenance - retour d'expérience, par Oana Juncu, et
  3. La démarche Agile: contexte favorable/défavorable
  4. Pratiques d'ingéniérie incrémentale, par Eric Mignot et LaurentCobos, ainsi que  
  5. Spécifications exécutables avec GreenPepper, et
  6. Dojo Randori
  7. L'art du jonglage avec les pratiques XP pour le développement web/ PHP, par Perrick Penet
  8. Opentime et ses 10000 tests automatisés, toujour par Perrick,
  9. Intégration continue & Offshore, par Thomas Recloux,
  10. Eclipse + Mylyn : Se concentrer sur une tâche, par Dimitri Baeli,
  11. et un atelier de Planification Agile par Patrice Petit.

Les sessions étaient découpées en créneaux de 30', plus 5' de temps élastique permettant de laisser aux gens de bouger.

Comme nous n'avions pas plus de sessions que de créneaux (18 au total), les participants ont "voté" en arrivant avec des gommettes pour nous permettre de répartir au plus juste les salles. La répartition des votes semblait indiqué en grand besoin d'information, plutôt que du pratiques, probablement parce que beaucoup de participantsn n'étaient pas des experts de l'agilité et venaient là pour découvrir, apprendre et surtout discuter.  

J'ai fait le planning après avoir introduit Agile Tour et les grandes lignes de l'organisation, tandis que les orateurs se présentaient, eux et leurs sessions.  

Les lieux

Nous avions donc trois salles:  

  • un grand amphi d'une centaine de places;
  • une grande salle de réunion pour 30 personnes environ;
  • une petite salle de cours d'une quinzaine de places.

Les locaux de TELECOM Lille 1 sont très bien, mais la disposition des salles, très éclatées les unes des autres n'étaient pas idéale. Heureusement, le fléchage était bien fait et les salles quand même situées dans des endroits évidents, pas au fin fond d'une aile. Personne ne semble s'être perdu, ou du moins pas suffisamment longtemps.  

Les participants

Il y avait 85 inscrits, et il me reste 22 badges, donc j'en déduis qu'il y avait au moins 63 participants. EN réalité, il devait y en avoir légèrement plus puisque des gens non inscrits se sont présentés spontanèment.  

Les profils étaient très variés. Je n'ai pas de statistiques précises, dont le sens sur un échantillon de 60 personnes serait de toute façon douteux, mais j'ai perçu les grandes lignes suivantes:

  • pas mal de développeurs,
  • quelques managers, chefs de projets, DSI. Peu nombreux maisintéressés par le sujet et demandeurs de billes pour faireprogresser l'agilisme dans leur structure,
  • peu d'étudiants m'a-t'il semblé,
  • des belges en nombre relativement important. Perrick m'a dit avoirfait passer le message (je regrette d'autant plus le forfait dePascal et Yves Hanoulle).

Le déroulement

Tout le monde a salué l'organisation de l'événement, ce qui ne laisse pas de me surprendre car j'ai vraiment eu l'impression qu'on a fait ça à l'arrache et que c'était un peu... chaotique. Je pense, cela dit, que beaucoup ont apprécié le côté informel. Le groupe était de petite taille, la manifestation de courte durée et le lieu compact, donc l'improvisation pouvait suppléer à la préparation.

Je n'ai eu que de bons échos, aussi bien de la part des participants que des orateurs. Il faudrait avoir dépouiller les questionnaires de satisfaction pour en savoir plus. J'ai le sentiment que tout le monde a trouvé ce qu'il venait chercher ou a pu dire ce qu'il avait à dire.  

Je regrette un peu le manque d'ateliers et la faible participation au dojo présenté par Eric Mignot et Laurent Cobos. Il me semble pourtant de plus en plus que le développement de l'agilisme passe par là, que c'est un outil extraordinaire pour progresser et faire progresser. Comme si beaucoup de gens avaient un peu peur de se mettre en danger en codant collectivement, comme si le code était toujours quelque chose que l'on faisait solitairement.

Des idées pour plus tard

J'ai vraiment envie de trouver d'autres formes que la classique conférence où l'on vient écouter la bonne parole, confortablement assis dans un amphithèâtre. Il me paraît essentiel, partie intégrante des valeurs et des principes de l'agilité, que les participants contribuent activement au contenu de la conférence.  

Les ateliers sont un bon moyen de parvenir à ce résultat, et en discutant avec Marie-Aimée, nous avons imaginé d'autres types d'organisation:

  • un débat modéré en amphitéâtre avec filmage en direct. Unfacilitateur transmet le micro, donne la parole. Un cameraman cadreles interventions qui sont visibles immédiatement dans la salle etdans le forum. Des orateurs ou experts sont répartis dans la salle,pour éviter la séparation introduite par la scène.
  • un montage en direct des ateliers et sessions filmées. Toutes lessessions sont filmées et directement mixées et montées en régie,pour être projetées dans le forum,
  • la mise à jour en direct d'un site web avec photos, vidéos,scannages de paper-boards et de notes, interventions et blogs desparticipants et orateurs. Un CD est réalisé à la fin de laconférence contenant le site et le contenu produit
  • l'utilisation d'un moteur d'analyse sémantique pour relier,regrouper et indexer les interventions sur le site.
  • le site de la conférence pourrait être mis  à jour par différentscanaux: twitter, RSS, mails, blogs...

20081015: On code quality and TDD

An old post by Michael Feather that talks about things I would like to present at SPA2009: different approach to code quality than TDD. Here it talks about the Clean Room method that, back in the 80's, enforced quality through formal expression of code behavior with logical predicates (presumably Hoare's logic predicates).

20081009: Web application - Part 3

Things are getting more and more complicated as I am closing in on the client-side of the web app. Thanks to some help from the haskell-cafe mailing list denizens,  I managed to got a running FastCGI web application working, with the shared state passed around, although this feature is untested.

The basic idea is to initialize the World once in a shared variable reference, preferably a thread-safe one, then to pass that value to the worker functions that could well be some IO threads.  

previous
main = do world <- newTVarIO initialState  -- initialize shared variable containing state
          runFastCGI  $ work world         -- service requests 


work :: TVar World -> CGI CGIResult
work world = do sid    <- fmap (maybe 0 id) $ readInput "sessionId"      -- extract session identifier
                sname  <- fmap (maybe "index" id) $ getInput "service"  -- extract service name
                world' <- liftIO $ atomically $ readTVar world                     -- extract state of the world
                setHeader "content-type" "text/plain"
                output $ service sname world'

I am presently struggling with the client side, trying to use jQuery and its Form plugin. I found the hard way that although they are somewhat equivalent, and sometimes use equivalently in HTML, id and name are not the same. jQuery selectors use id while form submission uses the name attribute. So one usually define both or use [name='toto'] selector to extract the needed  field.  

I am still stuck with the intricacy of submitting forms, between the subtleties of submit(), ajaxForm() and ajaxSubmit(), but I can now pass around values from the client to the server, and most notably the requested service and probably the session id if it is defined.  

I plan to make the application in client/server architecture, with the Haskell code providing services and JSON data, and the client code using jQuery to update the UI.

20081007: State Transformers again

I managed to refactor the code to use explicitly State and StateT monad, thanks to this thread on Haskell-café and this blog post. In the process I got to understand a few things about those dreaded monads and the way to combine them, although this obviously only scratch the surface of things.

The code of authenticate now looks like:

previous
getUser :: String -> String -> IOWorld (Maybe User)
getUser login password = lift $ readResource (User login password)


authenticate :: String -> String -> IOWorld (Maybe Session)
authenticate login pwd = do u <- getUser login pwd
                            liftState (createSession [("unregister", unregister),("logout", logout)] 
                                       (checkPassword u))

All top-level actions are done in the IOWorld context which is a State monad encapsulating IO monad. We need IO because we are dealing with the persistence layer.

The tests look a lot nicer, although some duplication obviously persists:

previous
aLoggedInUserCanUnregisterWithKey =
    "Logged in user can access unregister action with key" ~:
          (withDB "test.db" 
               (evalStateT (do register "nono" "password"
                               Just session <- authenticate "nono" "password"
                               let Just action = lookup "unregister" (actions session)
                               action session
                               authenticate "nono" "password") initialState >>=
                   assertEqual "unexpected user" Nothing))    

The sequencing of actions that the monad allows - and imposes - is not cluttered anymore by explicit arguments passing. The extraction of the action could encapsulated too in a nice monadic action.  

The important lesson about monads is: you can't mix apples and oranges. Monad sequencing is just a special kind of composition:

  1. simple functions `f: a \rightarrow b` and  `g: b \rightarrow c`are composed using . operator, yielding function  `g . f: a\rightarrow c`
  2. functions with effects `f: a \rightarrow m b` and  `g: b\rightarrow m c` (ie. functions within monads) are composed using >> and <code>>>=</code> (ie. bind) operators, yieldingfunction `f join g: a \rightarrow m c`

The important thing to note is that both encapsulated type and monad type have to be compatible (ie. the same) for this to work. One cannot compose actions occuring in different monads without some kind of transformation which is just why monad transformers are useful: they allow one to unify different kind of actions/effects within a single layered monads.  

One important function in this context is the lift operator that allows, as it name implies, lifting functions operating within one monad to another layer of monad. This means that given a  IO a action, you can lift it to a StateT s IO a action by  lift, but given a State s a action, how do you lift it to StateT s IO a ? You need to apply the IO monad to the result of the  State monad before returning it.  

One solution given by the cited posts is to define a liftState function:

previous
liftState :: Monad m => State s a -> StateT s m a
liftState s = do state1 <- get
                 let (result, state) = evalState (do result <- s
                                                     state <- get
                                                     return (result, state)) state1 
                 put state
                 return result

But there is another, more elegant, solution: use the interface MonadState to constrain the context of the createSession function and all other functions that operate at the state level and could need to be lifted at some other level. We can thus now write:

previous
createSession :: MonadState World m => [(String,Action)] -> Maybe User -> m (Maybe Session)


authenticate login pwd = getUser login pwd >>=
                         (createSession [("unregister", unregister),("logout", logout)] . checkPassword)

I can understand this barely but will try to explain it:

20081006: StateT monad transformers and IO

While working on Haskell implementation for Colivri, I got stuck within the complexity of monad transformers, most specifically with the wiring of the State monad with the IO monad. The code uses state to store session informations when users log in and out of the system. For example, the authenticate function looks like:

previous
authenticate :: String -> String -> World -> IO (World, Maybe Session)
authenticate login pwd s = readResource (User login "") >>= 
                           return . (createSession s [("unregister", unregister),
                                                      ("logout", logout)] . checkPassword) 
     where
      checkPassword Nothing = Nothing
      checkPassword u@(Just (User uid pwd')) | pwd' == pwd = u
                                             | otherwise   = Nothing

The StateT is defined as

previous
newtype StateT s m a = StateT (\s -> m (a,s))

so I am tempted to rewrite authenticate as:

previous
type IOWorld a = StateT World IO a
authenticate :: String -> String -> IOWorld (Maybe Session)

The problem is that I am not able to write the code that would make this to typecheck or compile !

Otherwise, I managed to write a simple command-line interface through which I can login/logout and register/unregister users within the database. Next step is to wire that logic through FastCGI. But how do we maintain state on a FastCGI server ?

20081003: jsUnit and Callbacks

In a recent entry of Testing in the Toilets, a technique is described for testing asynchronous handlers tied to setTimeout et al. family of functions, available at the global scope. This is something I tried to do in Benefit for the scheduling code of test executors, but much better as here the passing of time is controllable.  

20081002: Web Application in Haskell  - Part 2

Database

I spent part of the morning switching from flat file storage to DB storage for the tiny little embryonic web app in Haskell I am working on. I managed to use Sqlite3 bindings for HDBC which is quite low-level but at least works smoothly once you recall how to code in SQL.  

Installation was easy once I removed the .cabal and .ghc directories from my $HOME

sudo aptitude install libghc6-hdbc-sqlite3-dev

I then made User type an instance of IResource and implemented the CRUD operations there:

previous
import Database.HDBC
import Database.HDBC.Sqlite3


...
handleError str err = error $ str ++ ":" ++ ( seErrorMsg err) ++ ", code:" ++ show (seNativeError err)


instance IResource User where
    keyResource User {login = uid} = uid
    writeResource User {login = uid, password = pwd} = 
        handleSql (handleError "in write resource") 
                  (do con <- connectSqlite3 "test.db"
                      run con "insert into auth_user (username, password) values (?,?);" [SqlString uid, SqlString pwd ]
                      commit