Teams x Progetti

CodicePlastico è una software house nel classico senso del termine: il suo unico scopo di business è soddisfare i bisogni dei suoi clienti mediante programmi scritti ad-hoc. Non rivendiamo, customizziamo, modifichiamo software fatto da altri ma scriviamo sempre tutto da zero. Oggi siamo in 15 persone, 14 sviluppatori e una persona che si occupa della parte amministrativa/gestionale/organizzativa dell’azienda. Queste 15 persone seguono diversi progetti. I progetti arrivano, iniziano, qualche volta finiscono molte volte continuano per anni e la cosa più difficile per noi è ottimizzare lo scheduling delle persone sopratutto per progetti che sono nella fase di coda lunga (principali sviluppi terminati e attività manutenzione, correzione e piccole aggiunte in corso) Fino ad oggi abbiamo fatto in modo che ogni progetto sia assegnato ad un microteam di 2-4 persone e che queste persone seguissero più di un progetto per contemporaneamente, tipicamente un progetto principale e uno o due progetti secondari su cui fare piccole manutenzioni. Questo meccanismo crea incroci difficili da sbrogliare soprattutto quando i numeri crescono. Capita che i team si “rubino” le persone a vicenda per poter terminare il progetto o effettuare un veloce intervento.

Uno dei modelli ideali, raccontato anche da alcuni coach è quello in cui tutti possono lavorare su tutti i progetti cosi da poter modulare l’effort a seconda delle esigenze. Ma anche questo, con i nostri numeri, diventa molto difficile, soprattutto per progetti grossi con una lunga storia. Se poi aggiungiamo il fatto che abbiamo in produzione diversi stack tecnologici le cose sono ancora più complesse.

Un’idea ci è venuta ripercorrendo la storia di codiceplastico e ricordando quando questo per noi non era un problema. Era il periodo in cui eravamo 5-6 persone, tutte commitate su tutti i progetti (che al tempo non erano molti come oggi) e tutti in grado di intervenire in caso di necessita’.

Ci abbiamo pensato, abbiamo valutato varianti e il modello che si sta concretizzando è quello in cui tutti possono lavorare su tutto ma su una scala ridotta. Stiamo cioè provando a creare tante (tre?) piccole CodicePlastico che siano autonome, per buona parte isolate dalle altre e responsabili di un sottoinsieme (proprio) dei progetti di tutta l’azienda. In questo modo ogni unita’ potrà seguire un piccolo set di progetti e su quelli avrà tutta l’autonomia necessaria, tutti i membri dell’unita’ saranno in grado di metterci le mani sia come per lo sviluppo che per la manutenzione.

L’idea ci piace e sembra, in linea teorica, funzionare correttamente. L’unico drawback che stiamo vedendo e’ il fatto che con il tempo potrebbero crearsi dei silos isolati che non comunicano tra loro essendo di fatto piccole aziende separate. Per far fronte a questo ipotetico problema abbiamo pensato di predisporre un programma di rotazione delle persone: una volta ogni tanto (stiamo ancora cercando di capire qual e’ l’intervallo di tempo giusto) una persona si sposta da una unita’ ad un’altra in modo che nel giro di 12-18 mesi tutti siano in un nuovo team.

Questo e’ l’ennesimo esperimento che stiamo conducendo per provare a diventare ancora migliori di quanto siamo oggi.

Remotizziamoci!

Il 2018 è stato per CodicePlastico un anno in cui sono maturatemolte nuove idee, mi piacerebbe raccontarle sia per riuscire a ripercorrerle e metabolizzarle sia per condividerle con la speranza che possano servire anche ad altre aziende o professionisti.

Uno degli obbiettivi che avevamo fissato a gennaio 2018 era trovare un nuovo ufficio più grande e spazioso per tutti. Fino ad allora eravamo stati all’interno del CSMT, un consorzio di aziende dell’innovazione bresciana, ma gli spazi disponibili non erano sufficienti a contenere 15-20 persone quindi o ci splittavamo in più uffici oppure dovevamo cambiare. L’idea di cambiare ci piaceva e a metà anno abbiamo iniziato la ricerca affidandoci ad “esperti” agenti immobiliari. Oggi, un anno dopo, siamo ancora qui!

Cosa è successo nel frattempo? Trovare un ufficio che ospiti 15-20 persone è una soluzione valida ma non è definitiva, chi può dire se tra 5 anni sarà sufficiente o sarà troppo piccolo ( o troppo grande)? Un nuovo ufficio implica grosse nuove spese: trasloco, adeguamento, nuovo canone d’affitto, attivazione dei contratti. Cercare un nuovo ufficio è dispendioso in termini di tempo ed è dannatamente noioso avere a che fare con gli agenti immobiliari.

Fatte queste considerazioni verso la fine dell’anno ci siamo detti: e se rimanessimo qui? Basterebbe far si che tutti possano lavorare da casa e che l’ufficio diventi un’opzione da usare solo qualche volta alla settimana. Nessuno avrà più la sua scrivania e le postazioni saranno libere. L’idea ci è piaciuta, ne abbiamo parlato con i colleghi e anche loro hanno apprezzato la possibilità. Ma, come Jacopo insegna, prima di effettuare una cambiamento vanno fatti degli esperimenti per validare l’idea. A novembre abbiamo istituito il remote-day: il giovedì tutto il team di CodicePlastico lavora da remoto. Ci serviva per capire se l’idea poteva funzionare o se c’erano delle grosse inefficienze da colmare che impedissero l’idea di remotizzarci quasi completamente.

Oggi, qualche mese dopo l’esperimento possiamo dire di essere soddisfatti, per molti di noi l’opzione remoto è diventata una buona abitudine e ci prendiamo sempre un paio di giorni per lavorare da casa. Sicuramente abbiamo ancora qualche dettaglio da migliorare ma tutto il team ne è consapevole e tutti sono committed per risolversi e proporre miglioramenti. Abbiamo anche deciso di riorganizzare l’ufficio per renderlo adatto al lavoro da remoto e al fatto che non avremo una postazione fissa per tutti.

Fp-ts validation spiegata bene

In CodicePlastico stiamo studiando la programmazione funzionale, abbiamo fatto un corso con Matteo Baglini (che consiglio a tutti) e in questi giorni stiamo provando a fare un po’ di refactoring verso il paradigma funzionale. Naturalmente i dubbi vengono, e ieri ho scritto una mail a Matteo per chiedere aiuto su una parte di codice dedicata alla validazione. La mail di risposta e’ stata talmente dettagliata che ho pensato fosse uno spreco tenerla solo per me, così (con l’autorizzazione di matteo) la pubblico qui:

[ema] Ciao Matteo, nell’addentrarmi nella programmazione funzionale in Typescript usando questa libreria https://github.com/gcanti/fp-ts mi sono imbattuto in questo meccanismo di validazione che non capisco completamente e mi sembra molto complesso. L’esempio particolare a cui mi riferisco e’ questo: https://github.com/gcanti/fp-ts/blob/master/docs/Validation.md


Ciao Ema,

Personalmente direi articolato non complesso. Più avanti mi spiego.

[ema] Perche’ si deve creare una funzione curried per costruire la Person?

In generale per costruire Person ti serve una funzione, questo banalmente perchè sei in fp e in fp tutto si fa con le funzioni. Le strutture dati algebriche (Person è un product type) non fanno niente se non immagazzinare dati. Le funzioni permettono la costruzione e/o manipolazione di questi dati. Ambienti come haskell e scala (con le case class) ti offrono out-of-box la funzione di costruzione. Typescript no, prima sfiga.

[ema] Allora perchè curried?

Questo accade perchè Applicative serva a comporre una funzione con effetti (le funzioni di validazione) + una funzione senza effetti con due parametri (la funzione di creazione di person). Nel mondo fp ovvero haskell una funzione con due paramentri (a, b) => c è di fatto (grazie a curry) 2 funzioni da 1 parametro a => b => c. Molto probabilmente Fp-Ts ha scelto di seguire più da vicino lo stile haskell, seconda sfiga. In scala gli autori di librerie tendono ad evitare il currying perchè non è naturale nel linguaggio.

Detto questo, perchè validatePerson nell’esempio di Fp-Ts è così complessa? Perchè ti mostra a basso livello come si compone una funzione con effetto con una funzione senza effetti con due parametri (curried).

Dissezioniamo questo mostro:

A.ap(validateName(input['name']).map(person), validateAge(input['age']))

la funzione ap è fatta così:

readonly ap: <A, B>(fab: HKT<F, (a: A) => B>, fa: HKT<F, A>) => HKT<F, B>

in scala

def ap: [A, B](fab: F[A => B], fa: F[A]): F[B]

in poche parole

  1. accetta fab: una funzione da A=>B wrappata in un effetto F aka F[A => B]
  2. accetta fa: un valore A wrappato in un effetto aka F[A]
  3. restutisce un F[B]

invocando le singole validazioni ottengo

const nameResult: Validation<NonEmptyArray<string>, string> = validateName(input['name'])
const ageResult: Validation<NonEmptyArray<string>, number> = validateAge(input['age'])  

A.ap(nameResult.map(person), ageResult)  

ora concentriamoci qui

nameResult.map(person) 

passando alla map (che accetta in ingresso una funzione con 1 parametro) il curried constructor ottengo

const nameResultBindend: Validation<NonEmptyArray<string>, number => Person> = nameResult.map(person)

dai! non ci credo! ho una funzione wrappata in un effetto sta a vedere che ora posso chiamare ap! ;-)

A.ap(nameResultBindend, ageResult)

applicando più volte la stessa tecnica (ap + map) posso comporre funzioni con 3/4/5/N parametri. Ripeto è un dettaglio low level, importante, ma low level.

Durante il corso per non andare così low level abbiamo usato la map2/map3/etc oppure la mapN. Prendiamo la map2, in scala la sua firma è:

def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z]

il suo scopo è quello di valutare in maniera indipendente 2 effetti (fa e fb), passare i risultati alla funzione f e liftare quanto prodotto (Z) nel solito effetto (F). Questo ci permetteva di fare una roba tipo

case class Person(name:String, age:Int)
def makePerson(name:String, age:Int):ValidationResult[Person] = 
   Applicative[ValidationResult].map2(validateName(name), validateAge(age))(Person.apply)

Quindi se vuoi usare questo tipo di combinatori (a più alto livello) devi cercarli in fp-ts. Ho fatto un giro e controllando la definizione di Applicative (https://github.com/gcanti/fp-ts/blob/master/docs/Apply.md) non ho trovato map2 ma una cosa equivalente liftA2: Lift a function of two arguments to a function which accepts and returns values wrapped with the type constructor F

ovvero data la funzione f: (A, B) => Z passandola a liftA2 otteniamo una funzione g: (F[A], F[B]) => F[Z].

nel mondo curry andiamo da f: A => B => Z a g: F[A] => F[B] => F[Z]

in pratica prendiamo una funzione senza effetti (f) e la trasformiamo in una che funziona allo stesso modo ma effectful.

Se caliamo liftA2 nell’esempio di validation di Fp-Ts dobbiamo prima di tutto prendere il curried constructor

const person = (name: string) => (age: number): Person => ({ name, age })

e liftarlo con liftA2 (non conosco typescript e fp-ts quindi vado un po’ di pseudo codice)

const A = getApplicative(getSemigroup<string>())
const personLiftata = liftA2(A)(person)

ed in fine validationPerson invocherà la versione liftata

function validatePerson(input: Record<string, string>): Validation<NonEmptyArray<string>, Person> {
  return personLiftata(validateName(input['name']), validateAge(input['age']))
}

sintetizzando prendiamo una funzione

string => number => Person

e “senza fare niente” la facciamo operare in un contesto di validazione

Validation<NonEmptyArray<string>, string> => Validation<NonEmptyArray<string>, number> => Validation<NonEmptyArray<string>, Person>

E qui sta la figata, ipotizziamo che validateName e validateAge siano implementate server side, quindi la validazione diventa asyn con i Task (sempre di Fp-Ts). Come cambia il tutto? La contruction function non cambia, non dipende da effetti.

Le validateName e validateAge le riscriviamo.

function validateName(input: string): Task<string> ...
function validateAge(input: string): Task<number>  ...

La definizione della personLiftata non cambia.

Mentre validatePerson cambia solo come tipo di ritorno

function validatePerson(input: Record<string, string>): Task<Person> {
  return personLiftata(validateName(input['name']), validateAge(input['age']))
}

Il tutto accade perchè prendiamo la solita

string => number => Person

e “senza fare niente” la facciamo operare in un contesto di HTTP async

Task<string> => Task<number> => Task<Person>

Forte, no?

[ema] E se avesse 20 campi?

Hai un problema di design! :-) Detto questo, esistono tante liftA con il numero di parametri

fp-ts arriva al max a liftA4. haskell arriva al max fino a liftA3 in scala arriviamo a map22 (siamo troppo avanti :-) scherzi a parte sono 22 perchè in java possiamo definire metodi fino ad un massimo di 22 parametri).

Quindi se cambiano i requisiti ed a Person viene aggiunto un campo “email” da validare dobbiamo:

  1. aggiungere il campo sul product type
  2. aggiungere il parametro curried alla constructor funcion
  3. definire eventuali nuovi validatori
  4. usare liftA3 invece di liftA2
  5. usare il terzo validatore in validatePerson

Meccanico e dal punto 2 in poi guidato dal compilatore, per questo lo vedo articolato, ma non complesso. Una volta conosciute bene le astazioni. :-)

Nel nostro caso le validazioni sono N e sullo stesso campo devo poter fare piu’ validazioni (ad. es. la stringa e’ una data e la data e’ maggiore di oggi).

ti fai una funzione di validazione da stringa a data poi una da data a data in pratica ti fai le singole “check” slegati da cosa andrai a creare

function validateNotEmpty(input: string): Validation<NonEmptyArray<string>, string>
function validateGtToday(input: date): Validation<NonEmptyArray<string>, date>  

dopo la validatione della data per la solution da stringa a data è solo la composizione dei check (faccio ancora un typescript “a braccio”)

function validateDate(input: string): Validation<NonEmptyArray<string>, date> 
              validateNotEmpty.flatMap(validateGtToday)

flatMap, aka composizione monadica, perchè prima devi poter passare da stringa a data e solo se riesci devi vedere se la data è maggiore di oggi, altrimenti non fare niente.

[ema] E in uscita vogliamo:

export type SelectedSolution = {
  from: Station,
  to: Station,
  outbound: TrainDateTime,
}
export type TrainDateTime = {
  date: Date,
  time: Time
}

la validazione della TrainDateTime è una composizione applicativa di validateDate e validateTime (che non ho implemetato…)

function validateTrainDateTime(input: Record<string, string>): Validation<NonEmptyArray<string>, TrainDateTime> 
    createTrainDateTimeLiftata(validateDate(input['date']), validateTime(input['time']))

idem per Station e SelectedSolution, comporre le altre funzioni (che erano a sua volta composte da altre funzioni)

function validateStation(input: string): Validation<NonEmptyArray<string>, Station>
    validateNotEmpty(input).map(createStation)

function validateSelectedSolution(input: Record<string, string>): Validation<NonEmptyArray<string>, SelectedSolution>  
    createSelectedSolutionLiftata(validateStation(input['from']),  validateStation(input['to']), validateTrainDateTime(input))  

:-)

Arcade in Codiceplastico

In CodicePlastico abbiamo sempre cercato modi per creare un ambiente piacevole in cui passare la giornata lavorativa. Sappiamo benissimo che il nostro lavoro di programmatori ha parecchi aspetti negativi legati principalmente al poco movimento e al prolungarsi delle ore passate a concentrarsi su un problema. Per questo, senza imporre regole o obblighi, nel tempo ci siamo inventati strumenti e metodi per migliorare la situazione. Abbiamo iniziato anni fa con le freccette, “sport” al quale ci dedichiamo giornalmente con sfide all’ultimo 20 triplo! Da un annetto circa abbiamo introdotto anche l’acquisto aziendale di frutta (esistono fruttivendoli che vendono su internet!) e non ci facciamo mai mancare un po’ di frutta di stagione da consumare durante i meeting interni o durante le pause. Ma la grande novita’ di questi gioni e’ lui:

Eccolo

Ci siamo regalati un cabinato brandizzato CodicePlastico per staccare la mente dal codice e ritornare ai tempi delle scuole medie con i videogiochi del passato.

Insomma le giornate lavorative sono diventate piu’ divertenti e tra un test verde e uno rosso abbiamo tempo per qualche sfida!

Lens in javascript

Qualche settimana fa per un progetto che stiamo sviluppando abbiamo dovuto interfacciarci con le API di wordpress per recuperare alcune informazioni dai post pubblicati su un blog. La struttura dati restituita dalle API di wordpress e’ abbastanza nested e ogni livello potrebbe non essere presente, questo comporta che prima di accedere ad un attributo figlio va controllato che il padre non sia undefined.

Per andare sul concreto questa e’ una parte del json che le API restituiscono:

const obj = {
  ...
  "media_details": {
    "width": 913,
    "height": 1345,
    "file": "2017\/07\/immagine1.jpg",
    "sizes": [{
      "thumbnail": {
        "file": "immagine1-150x150.jpg",
        "width": 150,
        "height": 150,
        "mime_type": "image\/jpeg",
        "source_url": "http:\/\/blog.my-site.com\/wp-content\/uploads\/2017\/07\/immagine1-150x150.jpg"
      },
    },{}]
  }
}

L’attributo che andava estrapolato e’ l’url dell’immagine thumbnail

La prima implementazione che ci e’ venuta in mente e’ questa:

if (obj.media_details) {
  if (obj.media_details.sizes) {
    if (obj.media_details.sizes && obj.media_details.sizes.length > 0){
      if (obj.media_details.sizes[0].thumbnail.source_url) {
        img = obj.media_details.sizes[0].thumbnail.source_url
      }
    }
  }
}

NB: sto andando direttamente sull’indice 0 dell’array thumbnails per semplificare l’esempio

Non e’ sicuramente un’implementazione elegante…ma funziona. Possiamo fare di meglio? A parte fare un po’ di microrefactoring che non porta molto lontano possiamo provare ad usare un costrutto della programmazione funzionale: le Lens (o lenti in italiano). Le lens sono delle functional reference ad un attributo di una struttura dati che permettono di leggerne il valore o di settarne il valore. Librerie come ramda.js hanno un set di funzioni pronte all’uso e il codice sopra puo’ essere riscritto in questo modo:

const imgLens = R.lensPath(['media_details', 'sizes', 0, 'thumbnail', 'source_url'])
img = R.view(imgLens, obj)

Decisamente piu’ semplice, leggibile e mantenibile. La funzione lensPath construisce il percorso per raggiungere l’attributo, la funzione view ne estrae il valore.

Le Lens sono molto utili anche per cambiare il valore di un attributo di una struttura dati tipo quella sopra tramite la funzione R.set di ramda.

Supponiamo di voler modificare l’attributo ricercato prima tramite la lens per ottenere un nuovo obj. Senza il costrutto lens dovremmo scrivere una funzione simile a quella usata per il get (anche se sarebbe meglio che la modifica crei un nuovo oggetto). Con le lens l’operazione di set diventa:

const newObj = R.set(imgLens, 'nuovo_url', obj)

Le Lens sono un costrutto molto utile anche se poco conosciuto e spesso annegato in librerie tipo Ramda.js. La cosa interessante oltre alla semplificazione che portano al codice e’ la peculiarita’ di essere funzioni che possono essere riutilizzate in varie parti del codice e che si possono comporre con altre per ottenere funzionalita’ piu’ complesse, cosa che si sposa benissimo con altri costrutti della programmazione funzionale.