Guillaume Maillard a écrit 268 commentaires

  • [^] # Re: Inférence de types

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de JDK 10. Évalué à 4. Dernière modification le 10 novembre 2018 à 11:26.

    LA question que je pose depuis le début et que tout le monde fait semblant de ne pas comprendre et d'éluder (avec un petit downvote au passage), c'est quand tu lis :

    auto counter=getNextValue();
    
    if ( counter == numeric_limits<decltype(counter)>::max() )
     throw overflow_error("counter too big");
    
    ++counter;

    comment tu connais le type de retour de getNextValue() sans aller voir sa déclaration (dans un autre fichier ou des centaines de lignes plus loin) ?

    Ce n'est pas en répondant que "le langage XY a aussi des var/auto" ou "j'y arrive" que tu réponds à la question. Idem pour Renaud. Vos postures a base d'argument d'autorité genre "tu l'utilises pas tu sais pas", "je m'en sers, ca marche", "OCaml c'est bien", c'est le genre de poudre aux yeux qu'on laissera aux politiques. Et n'allez pas penser que je suis réfractaire à la nouveauté, il y en a de très intéressantes, mais pas celle ci.

    De plus, je ne suis pas fan du code que tu proposes, tester les limites des types en dynamique alors que le type ne changera pas en runtime tout ca parce que c'est "mieux" d'écrire "auto counter" que "int counter", ca n'a pas de sens (et je ne parle même pas des performances…).

  • [^] # Re: Inférence de types

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de JDK 10. Évalué à 1.

    J'ai l'impression que par lisible tu veux dire "rapide à lire".
    Oui, "for (auto element : conteneur)" c'est rapide à lire

    Par lisible, j’entends compréhensible, et ceci ne l'est pas, car si la définition de "conteneur" est 40 lignes plus haut ou si c'est lui même un "auto" machin, le code n'est PAS compréhensible. Même pas en rêve avec tes technos dites "modernes" et les bonnes pratiques de mamie.

    Pour reprendre sur Java et ne pas digresser sur le C++ qui n'a jamais eu de syntaxe claire,

    for (String str : parts){
    }
    est parfaitement compréhensible, on a tout le contexte. On ne peux pas se tromper sur l'utilisation/le type de 'str'.

    Je ne dis pas que le compilateur va se tromper, c'est le programmeur qui ne peux pas s'y retrouver (à moins que ta "bonne pratique" c'est frde nommer les variables/les paramètres en incluant le type.
    genre :

    var myInt = getNextValue();
    Et ici, franchement, on a frôle la débilité profonde.

    Comme tu es sérieux et rigoureux, tu vas écrire pour ton compteur :

    var count = getNextValue();
    et peut être même l'incrémenter quelques lignes plus bas.
    Et comment tu vas être sûr que tu ne risque pas te faire un overflow?
    count c'est un "int", un "long" ? Ah, non pas de bol, il est lu d'un stream octet par octet, c'est un "byte".

    Conclusion, tu ne peux pas comprendre en lisant le code (faut aller voir la signature du getNextValue() pour savoir, une perte de temps), bref, c'est casse gueule au possible et complètement inutile (et c'est pas parce que Rust le fait que c'est bien).

    Cette "évolution" de Java est juste là pour faire plaisir aux amateurs de Javascript qui voudrait goûter à Java.

    (notons au passage que les gros projets Javascript migrent vers Cleartype pour justement avoir du code "lisible" et plus robuste que la loterie à base de "let" et "var")

  • [^] # Re: Inférence de types

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de JDK 10. Évalué à 0.

    C'est assez naïf comme raisonnement. Ce n'est pas parce que le compilateur connait le type que le "programmeur" oui.

    Suffit de passer ce "var" magique à une API qui a des méthodes de même nom mais avec des arguments de types différents pour être sûr d'avoir du code Java du niveau d'un code javascript…

    Ça va compiler sans problèmes, ça c'est sûr!
    Je ne parle même pas des problèmes de refactoring que cela implique.

    L'argument C++ et autre, je vois pas le rapport, en C++ on peut aussi se contenter de void** !

    code plus lisible et maintenable,

    C'est tout le contraire, on ne sait plus de quoi on parle:

    var toto = getNextValue();
    Tu m'explique comment tu arrives à mieux lire le type de toto pour savoir quoi en faire 3 lignes plus bas?

    var titi = toto.apply();
    toujours sûr de savoir de quoi on cause?
    Je suis très curieux de voir un exemple réel…

    en évitant les noms à rallonge

    on parle de type, pas de nom de variable ou de paramètre, gagner quelques octets dans un code source n'apporte rien à moins d'avoir de sérieuses douleurs au doigts.
    Aux dernières nouvelles, le temps à "taper" le code est ridiculement faible par rapport au temps à passer à réfléchir sur l'architecture, les algorithmes, bref à quoi frapper sur le clavier…

  • [^] # Re: Inférence de types

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de JDK 10. Évalué à -1.

    C'est surtout aussi inutile que casse gueule… le typage fort c'est un avantage pas un inconvénient.

    Quand je lis "certains types peuvent être soit inconnus du programmeur", je comprend qu'on est pas en train de parler de programmation mais de bidouille, d'un code mal maitrisé qui a toute les chances d'être buggé jusqu'au trognon.

  • # Un peu d'air frais

    Posté par  (site web personnel) . En réponse à la dépêche Haiku R1 bêta 1. Évalué à 10.

    Après l'avoir utilisé quelques heures, je ne peux que vous conseiller de le tester, ça fait du bien, l'ensemble est stable (sauf peut être le navigateur qui plante aléatoire sur Youtube).

    L'avantage c'est qu'étant basé sur une architecture performante d'il y a 20 ans (1 thread par fenêtre par exemple), et bien que non fortement optimisé, tout est très rapide et peu gourmand en mémoire.

    Graphiquement, ce n'est pas aussi bon que l'original, les marges et alignements ne sont pas terribles, le look "glossy" (que n'avait pas BeOS) fait un peu désuet, mais rien compliqué à corriger.
    J'espère que le "subpixel rendering" des polices arrivera bientôt.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 5.

    Tu as tout à fait raison, les interfaces sont plus élaborées maintenant, mais je nuancerai ton propos en te donnant rendez-vous dans 1 an.
    En effet, dans un an, les développeurs de GNOME auront passés des milliers d'heures sur leurs projets, les attentes des utilisateurs n'auront pas bougé, les matériels n'auront pas été révolutionnés.
    Et pourtant, je prends le pari que la situation n'aura pas évoluée en termes de qualité et de performance.
    Par contre on aura le droit a de nouvelles icônes ou des bizarreries d'ergonomie, GNOME a un passif de 20 ans.

    On pourra toujours me reprocher d'être trop corrosif avec GNOME, mais quand ton job et ta passion c'est le développement logiciel, au bout de 25 ans, voire que la qualité a tendance a se dégrader et qu'il y a toujours quelqu'un pour le justifier avec un aplomb fabuleux, ça me fatigue.
    Par "faire du code rapide, pas cher et de qualité, ça n'existe pas", si tu veux dire que dans un temps donné, un développeur ne peut pas écrire du code performant (quasi) sans bugs, c'est difficile à justifier.
    Car cela revient à dire que tous les développeurs ont le même niveau. J'ai croisé assez de développeurs bien meilleur que moi et aussi bien pire, pour te garantir que je peux faire du code moins rapide, cher et de moins de bonne qualité que certains.
    La capacité à écrire du code rapide et de qualité est, de mon point de vue, surtout lié à l'expérience (formation, temps passé sur le même sujet, etc) et l'attention portée aux détails.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 1.

    Bizarre de me prêter des affirmations que je n'ai pas émises pour défendre coûte que coûte un logiciel mal codé (gdm).

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 2.

    Et pourtant vrai, si tu es développeur, tu peux mettre quelques "printf" pour t'en rendre compte.

    Quand tu ouvres un dossier, les droits sont analysés (pour mettre des petites icônes d'interdiction), les icônes sont associées aux types des fichiers et disposées en fonction de la taille de la fenêtre, tout cela dans le respect du "thèmes", de tes préférences etc…etc…

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 4.

    Je ne suis pas au courant d'un accident nucléaire causé par un bug logiciel, ni d’appareil médical qui plante tous les jours…
    Surement que les logiciels qui gèrent les plannings ou autres ne doivent pas être au top niveau, mais je ne faisais pas référence à cela.

    Pour les jeux, tu crois vraiment que c'est des quiches qui codent les moteurs 3D ?
    En 10ms, un développeur de Gnome n'est pas foutu d'ouvrir un dossier, les moteurs 3D s'occupent de gérer des millions de polygone, des gigas de textures, etc…
    Je t'invite à regarder les publications/vidéos de Mike Acton par exemple (Data Oriented Programming) ou de Jonathan Blow (qui travaille sur le compilateur de son langage Jai). Le compilateur Jai compile 20 000 lignes de code par seconde, ce qu'il trouve encore lent par rapport à ce qu'il pense possible de faire (x2).

    Le bugs auxquels tu fais référence concernent une minorité de jeux, et souvent ceux dont la date de sortie est définie avant que le jeu soit terminé. Quand les studios laisse la possibilité au devs de finir le job, en quelques mois tout est corrigé/terminé.

    Gnome, lui, n'a pas de réelle deadline. Une version "stable" sort quand elle est décrétée stable. Gnome a nécessité 20 ans de travail pour en arriver où il en est. Il ne manque pas ici de commentaires pour asseoir (ansi que leur bugtracker) que la stabilité n'est pas son fort. Personnellement, je ne faisais juste qu'une parenthèse sur la gloutonnerie de ressources.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 2. Dernière modification le 22 septembre 2018 à 14:26.

    Oui c'est un constat général dans le monde du logiciel.

    Il a toutefois des exceptions dans le monde :
    - de l'embarqué (car les ressources sont extrêmement limitées),
    - des applications critiques (pour la santé, le nucléaire… car des vies sont en jeu),
    - des jeux "AAA" (car il faut sortir la plus "belle" des images en moins de 10ms),
    - du calcul scientifique (car les minutes d'utilisation de supercalculateur sont chères)

    C'est aussi une culture, pour un tout jeune "développeur" web, la conso mémoire c'est un concept lointain, il sait tout au plus que sa page web va consommer 300Mo de mémoire vive dans Chrome et pour le CPU, il ne prendra "que" 10% d'un cœur de son Core i7…

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 3.

    Rappelons que stocker en mémoire une image brute (bitmap) 4k en 32bits, nécessite 32Mo.

    Un "desktop" a peine booté qui consomme 1Go, cela n'a rien de normal même en 4k.
    La crypto, la gestion de l'autonomie, l'usb ou n'importe qu'elle techno que vous allez trouver ne justifie pas cela.
    Le poids des images vectorielles c'est peanuts par rapports à leurs équivalents bitmap.

    La "gestion de l'autonomie", la crypto et le rendu vectoriel sont pris en charge matériellement depuis des années et au dernières nouvelles. Ce n'est pas le boulot de Gnome d'implémenter ces points et ils n'ont pas d'impact majeurs sur la consommation CPU.

    Je ne vois pas pourquoi un gestionnaire de session comme gdm aurait besoin d'un bitamp 4k en permanence… mais bon, si cela vous fait plaisir de vous conforter dans l'idée que les développeurs de Gnome sont à la hauteur, pourquoi pas.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à -1.

    Que l'OS ne protège pas (par de la mitigation ultra sophistiquée) les applications mal codées n'en fait pas un OS non fiable ou non sécurisé.

    Il a été conçu principalement pour le marché des systèmes embarqués industriels et médicaux, qui ont besoin de fiabilité et du temps réels.
    Des systèmes fermés difficilement piratables car n’exécutent pas de code externe.
    Comme le document de l'article l'explique, les processus sont complètements isolés et il ne peuvent pas planter le système. Et il n’intègre pas de blague comme le tueur aléatoire de processus de linux "en cas de besoin".

    Les commentaires sur OSNews semblent aller dans ce sens aussi.

    Bon après, c'est sûr que si tu lances gnome/gdm en root dessus, QNX ne ferra rien de magique :)

    Mais n'étant pas non plus un fanboy de QNX, je ne chercherai pas d'arguments bidons pour excuser tout bug/faille de QNX (NDLR : comme d'autre l'ont fait avec gdm…). On ne peut que souhaiter qu'ils intègrent des mitigations plus poussées. Ainsi les billes qui lancent leurs applis buggées en root, pleines de failles exploitables à distance auront moins de chances d'engendrer des morts… si des hackers ont envie de pirater leur propres assistants respiratoires, c'est pas trop mon problème.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 2.

    Les guillemets c'est pour les anglicismes ou les notions un peu "borderline" ou flous.

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 2.

    Depuis le début je parle de gdm , le "truc" qui consomme plus qu'un os complet optimisé…

    Alors bon, ton excuse à base d'USB, de PCIe, etc.. tu ne dois pas ignorer que gdm ne gère pas ça.
    Il n'a pas non plus besoin d'une machine qui doit afficher des jeux videos en 4k, etc..

    Pour la sécurité, la fiabilité, concernant QNX, tu repasseras, il est utilisé dans un bon nombre de systèmes critiques et il a fait ses preuves. Il est codé en langage C (et non en langage "forceps"), tout comme gdm…

    Comparer à 20 ans n'est pas ridicule, vu que les gestionnaires de sessions existaient déjà et que les "nouveaux" ne font pas grand chose en plus.

    Pour en revenir à ce dernier, presque 20 ans de dev pour gdm :
    - juste 3192 bugs
    - 26 071 lignes de code C (1737 lignes pour les headers), avec 54 fichiers .c

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à 4.

    Oui, on avait déjà un "desktop" utilisable sur des machines avec 1Mo de mémoire vive (sur Amiga par exemple)…

  • [^] # Re: Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à -1.

    Gnome (dont gdm fait partie) a presque 20 ans.

    Il n'était déjà brillant à ses débuts, si vous avez eu la chance d'utiliser les premières versions vous vous souvenez surement de la galère juste pour le compiler et le paramétrer.
    L'épisode "OpenedHand" n'a juste réussi qu'à dégrader "l'expérience" sur desktop, au profit du "mobile".
    Au final, on se retrouve avec, entre autre, un gestionnaire de session qui occupe en permanence des mégas octets de mémoire vive, et on se félicite d'avoir gagné quelques centaines de mégas octets.
    C'est sur que quelques mégas octets, ça ne représente plus rien, c'est juste la taille d'une page web… A réfléchir comme cela, on se retrouve avec un ordinateur qui démarre en plus d'une minute, et qui a déjà bouffé 1Go de mémoire… un milliard d'octet, rien que ça.

    Difficile, impossible, on a déjà la crème de développeurs chez Gnome?

    En 1999, QNX a sorti une version de son OS (v4) avec leur noyau temps-réel, les drivers (graphiques et réseau), un desktop et un navigateur internet sur une disquette de 1.4 Mo. Elle est encore téléchargeable : http://toastytech.com/guis/qnxdemo.html

    En 2002, ils récidivent avec un truc beaucoup plus léché (QNX RTP), la doc complète, les outils de développements (graphiques), un player audio, un navigateur rapide, etc… une ISO de 260Mo. Voir cet article de preview de la version 6.2 pour le détail et des captures d'écran.

    Bref, la transition "wayland" a bon dos.

  • # Super les gars

    Posté par  (site web personnel) . En réponse à la dépêche Parution de GNOME 3.30. Évalué à -2. Dernière modification le 20 septembre 2018 à 19:31.

    J’apprécie toujours les news du style "xxx Mio de mémoire vive libérés", ou encore celles que l'on trouve régulièrement sur les performances "xyz est désormais 3000x plus rapide".

    Le touriste pourrait se dire que c'est une super nouvelle, que les développeurs sont sacrements bons, qu'ils ont bossés dur pour arriver à ce résultat spectaculaire.

    La réalité est tristement moins joyeuse, s'il est possible de faire de telle "optimisation" c'est qu'à la base toute cette "suite" logicielle est codée par dessus la jambe.
    On ne peut qu'en conclure que leurs développeurs sont sacrements mauvais, qu'ils ont bossés un peu pour corriger leurs erreurs afin d'arriver à se résultat peu spectaculaire (gdm qui consomme 100Mo).

    Il n'y a pas si longtemps, on avait un "desktop" fonctionnel avec des machines de 128Mo de mémoire vive et moins de 400Mhz.

  • [^] # Re: Accès

    Posté par  (site web personnel) . En réponse à la dépêche Profileurs mémoire MALT et NUMAPROF. Évalué à 4.

    Le compilateur n'est pas encore assez intelligent pour optimiser les accès, il peut réorganiser "un peu" les structures de données.

    Mais pour des cas "simples" comme celui : multiplication de matrice

    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            for (k = 0; k < N; k++)
                res[i][j] += mat1[i][k]*mat2[k][j];
        }
    }

    il faut un bon outil pour voir/comprendre le problème et écrire la version optimisée.

  • # Accès

    Posté par  (site web personnel) . En réponse à la dépêche Profileurs mémoire MALT et NUMAPROF. Évalué à 2.

    Maîtriser les allocations (nombres et tailles) est une chose, mais les gains importants en performance résident surtout sur les accès.
    Plus ils sont "distants" les uns des autres moins les caches matériels sont efficaces.
    De mémoire, il y a un facteur 200 entre la latence au niveau du cache L1 et la mémoire vive.

    J'espère qu'un jour on aura un outil pour aider à optimiser les accès.

  • [^] # Re: À la fois troll, à la fois fait divers

    Posté par  (site web personnel) . En réponse à la dépêche Faut‐il continuer à apprendre le C++ ?. Évalué à 1.

    Et pour les cas où il faut diagnostiquer des lenteurs sur un serveur sans rien toucher ni perturber : https://www.youtube.com/watch?v=tYieP2s34YI . Bon courage pour faire ça en C++ :)

  • [^] # Re: À la fois troll, à la fois fait divers

    Posté par  (site web personnel) . En réponse à la dépêche Faut‐il continuer à apprendre le C++ ?. Évalué à 3.

    Pas besoin de refaire la bataille, le C++ (et le C) est gagnant de quelques pourcents.
    Pour les calculs scientifiques, cela se vérifie avec scimark2 (FFT, calcul matriciel… ) http://scribblethink.org/Computer/javaCbenchmark.html

    Ne connaissant pas clion, je peux te répondre sur les 3 autres que j'ai pu utiliser pour de vrais projets. Leur debuggers ne rivalisent pas avec celui d'Eclipse, la raison technique est simple : le fait de pouvoir discuter avec la JVM permet de tout contrôler et d'avoir un état complet et fiable de tous les threads, des "stacktraces" et des objets.

    Pour JProfiler, c'est exactement la même chose, mais une vidéo sera plus parlante : https://www.youtube.com/watch?v=032aTGa-1XM et ce n'est qu'une toute petite partie des possibilités.
    J’apprécie aussi beaucoup d'avoir une vue d'ensemble chronologique (en temps réel) sur les attentes liés aux IO et aux locks.

    Pour Eclipse vs les autres, en C++ les IDE ne peuvent pas faire de miracle pour le refactoring ou la complétion à cause principalement des void** et des directives de preprocessing.

    Je pense que la seule vraie façon d’appréhender les avantages du trio Java/Eclipse/JProfiler est de l'utiliser dans un projet conséquent.

  • [^] # Re: Phobie du GC

    Posté par  (site web personnel) . En réponse à la dépêche Faut‐il continuer à apprendre le C++ ?. Évalué à 6.

    Le GC est pénalisant quand il prend beaucoup temps, cad quand il y a un très grand nombre d'objets à se débarrasser. Mais la cause reste tout de même un très grand nombre de créations d'objets dans un temps très courts.

    Allouer des objets par millions, c'est contre productif en C++ tout comme avec les langages avec GC (Java, C#, etc…) l'allocation mémoire est très pénalisante en C++ (moins rapide qu'en Java par exemple). De plus, la fragmentation mémoire augmente et l'utilisation de la mémoire vive aussi inutilement.

    Avoir un programme performant en C++ c'est surtout s'assurer de minimiser les allocations et d'avoir le plus possible un "layout" mémoire optimal (localité des données).

    En Java, il y a tout un tas de GC différents à utiliser en fonction des besoins.
    Je pense que les programmeurs utilisant Unity se définissent plus comme programmeurs C#, le moteur Unity étant manipulé en C# à la manière d'une librairie quelconque. Bref, à moins de vouloir recoder un moteur 3D, on peut très bien se passer du C++ ;)

  • [^] # Re: À la fois troll, à la fois fait divers

    Posté par  (site web personnel) . En réponse à la dépêche Faut‐il continuer à apprendre le C++ ?. Évalué à 9.

    La guerre du "c'est qui le plus rapide" entre C++ et Java est terminée depuis un bout de temps.

    Un code bien écrit en Java est en moyenne plus lent (de l'ordre de 10 %) car la JVM doit interpréter les instructions et compiler à la volée les méthodes les plus pénalisantes pour les performances.
    Je parle de moyenne ici, il y a des cas (rares) ou Java sera beaucoup plus lent qu'un code ultra optimisé en C++ mais il sait être plus rapide dans certains cas (car la JVM est capable de générer du code machine mieux optimisé car elle a plus de contexte à l'exécution qu'un compilateur), ce n'est pas un mythe.
    Les allocations mémoires en Java sont aussi beaucoup plus rapides qu'en C/C++.
    Des centaines de benchmarks sont disponibles sur internet pour qui chercherait des chiffres précis.

    Concernant le fait qu'il n'y ait pas de 'unsigned' en Java, c'est effectivement embêtant pour l'écriture du code mais en runtime cela n'a pas d'impact. Quand on pense que la gestion des 'unsigned' était prévue en Java 1.0 mais viré à la dernière minute car "pas le temps"…

    Et si on parlait aussi du nombre de crash sans stacktrace et du temps de développement lors de la création de votre programme C++ ?

    Avec 10 ans d’expérience en C++ et 20 en Java, de mon point de vue, le plus gros "gap" entre les 2 n'est pas tant le langage que les outils autours. Rien dans le monde C++ n'égale le trio Java/Eclipse/JProfiler.

    Faut-il apprendre C++ de nos jours : OUI. Cela permet d'apprécier Java ;)

  • [^] # Re: Lignes de code

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GIMP 2.10.2. Évalué à 7. Dernière modification le 26 mai 2018 à 18:16.

    900 000 lignes de code, je trouve cela juste gigantesque (c'est des années de travail à temps plein).

    Faut-il comprendre de vos explications qu'il a eu 911825 (921630-905) lignes de code supprimées? modifiées?

    De ce que je comprend sur https://www.openhub.net/p/gimp , le code actuel de Gimp représente :
    - 817267 lignes de code
    - 125212 lignes de commentaires
    - 190860 lignes vides

    D'où une certaine incompréhension (toujours présente).

  • # Lignes de code

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GIMP 2.10.2. Évalué à -3.

    921 630 lignes supprimées

    Juste pour pour la migration de vers GTK3 ?

    Euh.. comment dire…
    https://informationisbeautiful.net/visualizations/million-lines-of-code/