Best practice per la gestione del codice legacy

66

Tra un paio di mesi un collega passerà a un nuovo progetto e io erediterò uno dei suoi progetti. Per prepararti, ho già ordinato a Michael Feathers di Lavorare efficacemente con il codice legacy .

Ma questo libro così come la maggior parte delle domande sul codice legacy che ho trovato finora riguardano il caso di ereditare il codice così com'è. Ma in questo caso ho effettivamente accesso allo sviluppatore originale e abbiamo un po 'di tempo per un passaggio ordinato.

Qualche background sul pezzo di codice che erediterò:

  • Funziona: non ci sono bug noti, ma man mano che i requisiti delle prestazioni continuano a salire, alcune ottimizzazioni diventeranno necessarie in un futuro non troppo lontano.
  • Non documentato: c'è praticamente zero documentazione a livello di metodo e di classe. Ciò che il codice dovrebbe fare ad un livello superiore, tuttavia, è ben compreso, perché ho scritto per anni contro la sua API (come una black-box).
  • Solo test di integrazione di livello superiore: ci sono solo test di integrazione che testano l'interazione corretta con altri componenti tramite l'API (di nuovo, black-box).
  • Molto basso livello, ottimizzato per la velocità: Poiché questo codice è fondamentale per un intero sistema di applicazioni, molte di esse sono state ottimizzate più volte nel corso degli anni ed è estremamente di basso livello (una parte ha il proprio gestore di memoria per determinate strutture / record).
  • Concurrent e lock-free: anche se ho molta familiarità con la programmazione simultanea e lock-free e ho effettivamente contribuito con pochi pezzi a questo codice, questo aggiunge un ulteriore livello di complessità.
  • Large codebase: questo particolare progetto è più di diecimila righe di codice, quindi non è possibile che mi venga spiegato tutto.
  • Scritto in Delphi: lo spiegherò qui, anche se non credo che la lingua sia pertinente alla domanda, poiché ritengo che questo tipo di problema sia indipendente dalla lingua.

Mi stavo chiedendo come sarebbe stato meglio spendere il tempo fino alla sua partenza. Ecco un paio di idee:

  • Ottieni tutto per costruire sulla mia macchina: Anche se tutto dovrebbe essere controllato nel controllo del codice sorgente, chi non ha dimenticato di archiviare un file una volta ogni tanto, quindi questo dovrebbe probabilmente essere il primo ordine di affari.
  • Più test: Mentre vorrei testare più unità di livello di classe in modo tale che quando farò dei cambiamenti, qualsiasi bug che introduco possa essere catturato nelle prime fasi, il codice così com'è ora non è testabile (enorme classi, metodi lunghi, troppe dipendenze reciproche).
  • Che cosa documentare: penso che per i principianti sarebbe meglio concentrare la documentazione su quelle aree del codice che altrimenti sarebbero difficili da comprendere, ad es. a causa della loro natura di basso livello / altamente ottimizzata. Temo che ci siano un paio di cose che potrebbero sembrare brutte e che necessitano di refactoring / riscrittura, ma in realtà sono ottimizzazioni che sono state là fuori per una buona ragione che potrei perdere (cfr Joel Spolsky, Cose che non dovresti mai fare, parte I )
  • Come documentare: penso che alcuni diagrammi di classe dell'architettura e dei diagrammi di sequenza delle funzioni critiche accompagnati da una certa prosa sarebbero i migliori.
  • Chi documentare: Mi chiedevo cosa sarebbe stato meglio, per fargli scrivere la documentazione o farglielo spiegare, così posso scrivere la documentazione. Ho paura, che le cose che sono ovvie per lui, ma non per me, non sarebbero coperte in modo appropriato.
  • Rifattorizzazione utilizzando la programmazione a coppie: Questo potrebbe non essere possibile a causa di limiti di tempo, ma forse potrei refactoring alcuni dei suoi codici per renderlo più mantenibile mentre era ancora in giro per fornire input sul perché cose sono come sono.

Si prega di commentare e aggiungere a questo. Dato che non c'è abbastanza tempo per fare tutto questo, sono particolarmente interessato a come daresti la priorità.

Aggiornamento: Man mano che il progetto di handover è finito, ho ampliato questa lista con le mie esperienze in questa risposta sotto .

    
posta PersonalNexus 07.11.2011 - 20:44
fonte

11 risposte

25

Come hai accesso allo sviluppatore che richiedi codice: -

  • Quali moduli sono stati i più difficili da codificare / implementare. Quali sono stati i problemi e come sono stati superati.

  • Quali moduli hanno generato il maggior numero di bug.

  • Quali moduli hanno provocato i bug più difficili da risolvere.

  • Di quali parti di codice è più orgoglioso.

  • Quali bit di codice vorrebbe davvero refactoring, ma, non ha avuto il tempo.

Queste domande ti daranno un'idea di che cosa ti causerà più problemi e, forse ancora più importante, un approccio ai processi di pensiero e alle prospettive dello sviluppatore originale.

    
risposta data 08.11.2011 - 05:53
fonte
17

Poiché il progetto di trasferimento è terminato, penso che mi prenderò il tempo e scriverò la mia risposta contenente quelle che hanno funzionato meglio per me.

  • Ottieni tutto sotto il controllo della versione: Dopo esserti assicurato che tutto ciò che avevo bisogno di compilare era sotto controllo di versione, ho anche cercato sul disco rigido del vecchio sviluppatore, per cercare altri script o utilità che sarebbero stati utili da implementare e / o testare l'applicazione ma non sono stati registrati.
  • Top-down: vorrei iniziare con un look di alto livello per le classi principali e un tour guidato con il vecchio sviluppatore delle aree principali. Poi scaverei più a fondo nel resto da solo, segnalando cose che non avevano senso per me con //TODO marcatori.
  • Scrivi tutta la documentazione da solo: Mentre il vecchio sviluppatore ha controllato la mia scrittura per assicurarmi di aver capito bene, ho insistito per scrivere tutto da solo. In questo modo sarei sicuro che la scrittura avesse senso per me e non solo per il vecchio sviluppatore.
  • Commenti ovunque: ho aggiunto riepiloghi della documentazione XML a ogni classe e ogni metodo. In questo modo mi sono assicurato di aver almeno guardato ogni pezzo di codice e di avere abbastanza comprensione per riassumere quello che faceva in una frase. Ha inoltre reso più comprensibili i metodi utilizzando i metodi / le classi di riepilogo, in quanto IntelliSense acquisisce tali informazioni. Potrei anche facilmente identificare le aree del codice che dovevo ancora vedere.
  • Documento vicino alla fonte: per facilitare la connessione tra il codice sorgente e la documentazione, ho inserito la maggior parte della mia documentazione nel codice sorgente. Per la documentazione di alto livello che descrive l'interazione tra vari sottosistemi, ho usato un wiki, in quanto l'inserimento di queste informazioni in un solo punto del codice non ha funzionato. Tutta la documentazione dovrebbe essere ricercabile in formato elettronico e full-text.
  • Diagrams: Per una panoramica di base ho utilizzato diagrammi di classe di varie granularità per i diversi sottosistemi. Per le parti concorrenti, i diagrammi di oggetti e di interazione sono stati davvero utili; vedi anche la mia altra domanda sull'argomento .
  • Refactoring in coppia: Mentre eseguivo il refactoring con il vecchio sviluppatore per avere un'idea del codice e rendere le cose più manutenibili, questo era un processo lungo e rischioso, a causa della mancanza di buoni strumenti di refactoring e un mucchio di brutte dipendenze tra le varie parti. Lavorare in modo efficace con Legacy Code di Michael Feathers è un ottimo aiuto per questo, anche se il refactoring senza un adeguato supporto degli strumenti è ancora doloroso. Durante il refactoring gli avrei lasciato il controllo del mouse e della tastiera, perché era più divertente per lui (vedi anche il mio ultimo punto elenco) ed ero libero di annotare ciò che stavo imparando.
  • Separa i check-in per commenti e modifiche: dopo che ho introdotto accidentalmente un bug scrivendo un commento su un override , sono stato attento a inserire commenti e modifiche nei check-in separati. Ho usato una piccola utility per rimuovere tutti i commenti dal codice sorgente prima di controllare qualcosa, quindi una differenza di un check-in solo commento mostrerebbe 0 differenze. Tutte le modifiche (ad esempio la rimozione di campi inutilizzati) sono state attentamente esaminate da colleghi con il vecchio sviluppatore per assicurarmi che non stavo rimuovendo le cose che erano ancora necessarie.
  • Procedura dettagliata per riga dei passaggi critici: per i passaggi più ottimizzati / complessi, riporto il codice linea per riga con il vecchio sviluppatore e talvolta anche con un terzo collega. In questo modo ho avuto una conoscenza approfondita del codice e man mano che più persone hanno esaminato il codice, abbiamo identificato alcuni bug e alcune cose che potrebbero essere ulteriormente ottimizzate.
  • Sii veloce e continua a motivare il vecchio sviluppatore: ho notato che il vecchio sviluppatore era sempre meno interessato mentre il suo ultimo giorno si stava avvicinando (non a caso). Mi sarei quindi assicurato che le parti più critiche fossero consegnate per prime, lasciando il resto per me a capire da solo, se necessario. Ho anche provato a lasciare a lui le cose più divertenti (ad esempio il controllo della tastiera durante la programmazione in coppia) e fare cose noiose come la documentazione scritta da me.
  • Identificazione delle richieste di funzioni: ho trovato utile chiedere al vecchio sviluppatore un elenco di funzionalità richieste dalla gente ma che non sono state ancora aggiunte. C'erano alcune cose che a me sembravano semplici da aggiungere, ma dove c'era una buona ragione per non essere state aggiunte in quanto avrebbero rotto altre cose se implementate come pensavo all'inizio.
risposta data 01.02.2012 - 21:55
fonte
14

Essendo stato in una situazione simile, credo che anche la seguente considerazione meriterebbe considerazione:

  • Assicurati di poter realizzare e testare una distribuzione: fai la tua distribuzione del prodotto, da zero - e verifica che questo sia identico a uno fatto da la persona che sta partendo. Ciò assicurerebbe che gli script e le istruzioni siano chiari a voi e catturerebbero eventuali sviste accidentali come gli ingredienti che non sono stati registrati nel vostro sistema di controllo della versione. (Non sto dicendo che questo sarebbe accadrebbe, solo che se ha è successo, sarà molto più facile da gestire ora, prima che la persona se ne vada)

    (Potrebbe non essere pertinente per te, ad es. se fai già Integrazione continua o Distribuzione continua, ma vale la pena menzionarlo, nel caso in cui ...)

  • Scrivi altri test: questo è un veramente buon metodo per testare la tua comprensione di un sistema. Permetterà (o costringerà) a guardare più da vicino le aree del codice, e confermerà che il codice è privo di bug come sospetti o rivelerà le aree in cui hai pensato hai capito l'intenzione, ma in realtà è necessario chiedere chiarimenti al collega prima che lasci

  • Scrittura della documentazione in coppia: questo è un modo efficace per scrivere panoramiche. Sto suggerendo di far descrivere al tuo collega una funzione o un'area, e poi tu scrivilo, nella documentazione, con parole tue. Abbiamo trovato che questo era enormemente più facile se fatto da due persone insieme.

Metterei la scrittura dei test come una priorità più alta rispetto alla scrittura di documentazione, personalmente, poiché i test probabilmente ti daranno una comprensione più o più solida.

Riguardo a Refactoring using pair-programming , l'unica cosa che direi è che c'è il pericolo che questo possa diventare un pozzo senza fondo, specialmente dato che hai detto che hai solo un high - test di livello. Potresti scoprire che finisce per consumare più del tempo disponibile di quanto avresti voluto.

    
risposta data 09.11.2011 - 22:38
fonte
10

+1 per le risposte che hai già nella tua domanda!

Tour guidato
10k linee di codice sono molte, ma penso che non sia ancora impossibile avere l'altro ragazzo che ti dia una "visita guidata". Ti siedi insieme davanti al codice e ti porta in un viaggio dall'alto verso il basso, lavorando lungo gli 'strati'. Avresti bisogno di farlo in brevi raffiche - tutto in una volta ucciderebbe entrambi.

Zoom avanti, zoom indietro Il vantaggio di farlo è che, mentre ti spiega, probabilmente avrà alcuni momenti "oh, sì, c'è anche questo" che potrebbe non farlo se cercasse di documentarlo da solo. E le vostre domande aiuteranno a concentrarsi sui bit ovvi-a-lui-ma-non-a-nessuno-altro. Questo tipo di interazione di zoom in / zoom out è possibile solo uno contro uno, cercando di scrivere o leggere qualcosa di così difficile da gestire.

Documentazione
Penso che dovresti entrambi essere in grado di documentare in modo indipendente le cose - dovrebbe iniziare in fondo (nel caso non abbia il tempo di arrivarci insieme), e dovresti iniziare in cima, sulla base di ciò che hai capito della sua tour guidato e come se fosse per qualcun altro [in un precedente lavoro ho ereditato un carico di codice "legacy" e ho appena avuto il tempo di documentarlo prima di abbandonarmi:)].

Dove è cosa?
Lo scopo della maggior parte di questo è che tu possa essere in grado di capire dove succede qualcosa. In questo modo, dato un bug o una modifica particolare, puoi trovare molto rapidamente il posto nel codice su cui devi concentrarti. Puoi metterti alla prova prendendo la lista dei vecchi bug e vedendo se puoi prevedere con precisione dove si trova il problema.

Sparagli secco
Non importa se finisce per odiarti (sorride), il tuo compito è ottenere dal cervello di quel ragazzo più informazioni possibili nel tempo a tua disposizione. Assicurati di avere la gestione dalla tua parte e di dare priorità al trasferimento delle conoscenze "aggiustando gli ultimi bug prima che se ne vada" (a meno che non li risolvi tutti insieme ...).

    
risposta data 10.11.2011 - 08:49
fonte
7

Suggerisco quanto segue (oltre a ciò che è già stato identificato) - In primo luogo, chiedi al tuo manager di darti il tempo di lavorare con questo ragazzo il più possibile e cerca di sederti con lui ogni volta che gli viene chiesto di fare un cambiamento. Non devi sapere tutto quello che sta facendo, ma cerca di prenderne il più possibile. Più importante essere amici con lui.

Considera la consegna come un progetto e metti in atto un piano e coinvolgi la gestione.

0 - Assicurati di sapere come usare il sistema.

1 - Fai un inventario chiaro dei componenti della soluzione, la fonte di ciascuno e dove si trova (nei repository diff)

2 - Ottieni e, se possibile, gestisci, le password per i diversi server che iniziano ora. Assicurati di avere tutte le informazioni dell'account amministratore

3 - Ottieni le licenze di ciascun componente esterno a meno che non siano al di fuori del tuo ambito (ad esempio dll speciali, database, ecc.)

4 - Ottieni un rapporto scritto sullo stato attuale del sistema dallo sviluppatore e dai tuoi clienti (se sono locali alla tua azienda)

5 - Ottieni la documentazione per le regole aziendali, le formule di calcolo, ecc. Puoi farlo con lui. Chiedigli le e-mail, le informazioni sulle riunioni, i documenti sui requisiti degli utenti, i documenti di progettazione e simili da fornirti.

6 - Ottieni un elenco di eventi programmati (esecuzioni mensili di lavori, tirature settimanali di lavori) a cui il software deve rispondere

7 - Impara le procedure di backup / ripristino

8 - Comprendi i framework utilizzati nella costruzione dell'applicazione

9 - Scopri le modifiche richieste / previste / pianificate e lo stato di eventuali richieste degli utenti in sospeso. Inizia a cercare di identificare come farlo da solo.

10 - Assicurati che gli ambienti di test e di sviluppo siano molto simili.

11 - Cerca di identificare le principali dipendenze (su altri sistemi o tra componenti) che non possono essere facilmente individuati.

12 - Identifica e documenta le versioni richieste di ciascun utilizzo del software e il suo contatto con il venditore (se necessario)

13 - Identifica eventuali strumenti speciali che stava usando che non hai, nel caso in cui possa aiutarti.

14 - Ottieni un flusso di sistema di alto livello. e inizia a costruire la tua libreria di documentazione

15 - Scopri come gestire la sicurezza degli utenti per l'applicazione

16 - Ottieni il registro dei bug e cerca di capire le azioni e in che modo l'azione ha interessato i dati più vecchi (se applicabile)

17 - Conoscere processi che richiedono troppo tempo e che cosa è necessario controllare (ad esempio dimensioni di file insolite, ftp di file duplicati, ecc.), dove applicabile.

18 - Controlla l'orologio del server di produzione

19 - Identifica dove sono le configurazioni e confronta ogni configurazione di ambiente con la produzione per sapere quali sono i parametri diversi e perché

20 - Ottieni le informazioni di contatto di questo ragazzo

21 - Se il sistema è interno, pianifica una riunione con gli utenti del sistema (devi sapere chi sono e quale ruolo gioca ciascuno) e presentarti a loro. Ascolta cosa hanno da dire sul sistema e sui loro problemi attuali, se ce ne sono. Assicurati di essere incluso nelle e-mail il prima possibile (dopo l'approvazione del tuo manager)

22 - Valuta la tua comprensione 1 settimana prima di partire e segnala eventuali problemi che vedi come un rischio

Dato che hai menzionato che non hai un database, questo elenco si è accorciato.

Buona fortuna.

    
risposta data 11.11.2011 - 22:26
fonte
5

Per prima cosa considererei le parti più complicate e ottimizzate per le prestazioni. Vorrei fargli documentare prima quelle parti e spiegarle una alla volta, poi proverei a scrivere prove contro quelle parti (includendo prima e dopo i test delle prestazioni, così puoi vedere se una nuova ottimizzazione rende le cose migliori o peggiori ) e chiedi all'altra persona di rivedere i test. In questo modo, documenta e spiega, tu usi la spiegazione per scrivere dei test (mentre sta documentando un'area diversa), e la sua recensione ti aiuterà a capire che cosa dovresti testare. In questo modo si ottiene anche una conversione di test aggiuntiva per alcune delle parti più critiche dell'applicazione e la documentazione delle ottimizzazioni delle prestazioni specializzate.

Se c'è tempo dopo averli coperti, vorrei passare attraverso un processo simile con le parti dell'applicazione che hanno più frequentemente bisogno di cambiare negli anni ma che non sono nel primo gruppo di cose documentate.

Quindi documenta tutto ciò che è rimasto.

    
risposta data 07.11.2011 - 21:50
fonte
5

Penso che il modo migliore per ingannare qualche grande codice sia l'approccio top-down. Cerca di capire prima il quadro generale e poi di approfondire gradualmente i componenti uno per uno.

Ora ad ogni livello di scavo, chiedigli di dare la priorità alle parti che richiedono più attenzione. Fagli spiegare il più possibile, ma documentalo sempre da solo.

La parte migliore della documentazione di te stesso è che quando tornerai più tardi, non avrai problemi a ricordare lo stesso stato cognitivo in cui ti trovavi, quando te l'ha spiegato. Puoi capire molto più facilmente ciò che tu ha scritto rispetto a quello che ha fatto qualcun altro. Secondo la mia esperienza, due persone che documentano lo stesso codice non producono parti di testo simili.

Questo, immagino, risolve anche i tuoi problemi "cosa e come documentare". Quando ti spiega tutto, puoi decidere da solo che cosa vorresti fosse documentato quando tornerai al codice e documentare solo quelle parti.

L'idea è di capire prima completamente il codice (in sua presenza), e poi scrivere / fare tutto ciò che ti consentirà di farlo in seguito (in sua assenza).

Comprendendo completamente il codice intendo che hai bisogno di avere un'idea del quadro generale e di come ogni componente si rapporta a questo quadro generale. Ho trovato particolarmente utile tenere traccia di come ogni pezzo aggiunge il tutto. Non cercare di comprendere nulla in modo isolato - non perdere mai di vista il suo contesto.

Infine, una volta che hai fatto quanto sopra, prendi proattivamente il controllo. Decidi tu per quali pezzi hai bisogno di copertura per test unitari. quali parti devono essere (o possono essere) ottimizzate, come puoi refactoring di alcuni componenti, ecc. Affidati che se conosci il sistema, puoi prendere tutte le decisioni una volta che è andato.

    
risposta data 08.11.2011 - 07:33
fonte
5

Mi sento per te.

Alcuni suggerimenti:

  1. Tapea ogni conversazione che hai con il programmatore uscente!
  2. Chiedi la motivazione dietro le "grandi" questioni. È bene che tu capisca l'API, ma scavare per le decisioni interne - perché il codice è stato partizionato così com'è? quali sono le responsabilità.
  3. Sforzati di studiare davvero il codice. Quando si assumono compiti di manutenzione e supporto, a volte c'è la pressione di "studiare il codice mentre si fanno progressi". Resisti se puoi, e studia davvero il codice.
  4. Cerca scenari. Conosci l'API - guarda come si comporta il codice. Un esempio che viene in mente è quello di un modulo Fax. Come utente dell'API, i preheps dovevano preparare l'immagine di una pagina e inviare al codice un comando per trasmettere la pagina. Chiedere al programmatore in partenza di tracciare con voi sul codice per vedere come si svolge questo scenario. Quindi, ovviamente, vai allo scenario "pagina ricevente".
  5. 80/20 - prova prima a coprire gli scenari più comuni.
  6. Considera una riscrittura. Se il codice è vecchio e le interfacce sono ben definite, forse la tecnologia è cambiata abbastanza per giustificarlo.
  7. Detesto dire questo, ma considera la possibilità di cercare un nuovo lavoro.
risposta data 12.11.2011 - 19:50
fonte
3

Se vuoi una documentazione decente acquistare ragionevolmente una copia di Pascal Analyzer (PAL) ho usato questo sui progetti di Delphi ed è stato grandiose - potrebbero ora suddividere la funzionalità di documentazione in un prodotto che non conosco (Pascal Browser), quindi potrebbe essere necessario acquistare entrambi (< 300 USD) ma PAL era un ottimo strumento per capire dove venivano utilizzate le variabili dove venivano chiamate le funzioni da etc e amp; raccogliere tutti i tipi di potenziali problemi con il codice.

Usa PAL per farti un'idea di come il codice sia strutturato più probabilmente un elenco di circa 1000 miglioramenti suggeriti se la mia esperienza fosse qualcosa su cui andare avanti. Lavorare attraverso la lista migliorerà la qualità del codice, semplificandolo notevolmente e amp; rendi la tua vita più facile per il futuro. Delphi stesso supporta il refactoring nelle ultime versioni (gli ultimi 5 anni circa). Avevi bisogno di includere tutto nel file dpr perché funzionasse correttamente correttamente quando lo stavo facendo, quindi tienilo a mente.

Se desideri test unitari, scarica DUnit e amp; inizia a creare alcuni test con il codificatore originale: probabilmente è un modo costruttivo di utilizzare almeno una parte del loro tempo.

    
risposta data 10.11.2011 - 09:33
fonte
2

Anche se non hai menzionato un database di back-end ma ammesso che ce ne sia uno che dovrebbe

  1. Ottieni il modello dati documentato in particolare le colonne e PK-FK
  2. Imposta una traccia sql e registra tutte le query che vengono generate durante l'utilizzo dell'applicazione. L'ordine di esecuzione delle query ti darà una buona idea del flusso dell'applicazione e aiuterà anche nel debug
risposta data 10.11.2011 - 19:13
fonte
2

Sono nella stessa situazione in cui il nostro Architetto si è trasferito in Australia e ha lasciato molte eredità come lo era stato con la compagnia negli ultimi 8 anni. Lui stesso ha ereditato materiale ereditato dal precedente architetto che era un appaltatore.

Tu e altri avete già menzionato punti positivi, ma qui ci sono problemi che abbiamo affrontato dopo la sua partenza potrebbe essere che tu possa prepararti meglio ...

1) (Persona tecnica) Dettagli di contatto dei clienti con cui ha a che fare.

2) Il suo account con cui ha acquistato licenze software, chiavi che devono essere rinnovate ogni anno e processi / costi per rinnovarle.

3) Documento di installazione di librerie / componenti software di terze parti e prodotti che si integrano con i tuoi prodotti. Abbiamo faticato per 4 giorni a riportare una macchina che è andata persa a causa dell'eliminazione dello spazio da parte dell'IT e alcune istruzioni errate passate a loro.

4) Documenti / passaggi che viene utilizzato per depositare il codice sorgente a società di deposito del software, ad es. Escrow.

5) C'è ancora una lunga lista ma potrebbe non essere applicabile anche a te. Nessuna quantità di documentazione può sostituire una persona reale, quindi tieni i suoi dettagli a portata di mano, resta in buoni termini e buona fortuna:)

Inoltre non so se questa è la prima volta per te. Per quanto mi riguarda, ho lavorato con 5/6 datori di lavoro e ho sempre ereditato codice con cattiva documentazione o nessuna documentazione. Quindi, insieme a tutta la documentazione, resta positivo:)

    
risposta data 12.11.2011 - 12:37
fonte

Leggi altre domande sui tag