Zen go



En évaluant mon travail, j'ai récemment beaucoup réfléchi à la façon d'écrire du bon code. Étant donné que personne ne s'intéresse à la façon d'écrire du mauvais code, la question se pose: comment savoir si vous avez écrit du bon code sur Go ? S'il existe une sorte d'échelle entre le bien et le mal, alors comment comprendre quelles parties de l'échelle appartiennent au bien? Quelles sont ses propriétés, ses attributs, ses caractéristiques distinctives, ses modèles et ses idiomes?

Aller idiomatique


Ces considérations m'ont conduit au Go idiomatique. Si nous appelons quelque chose «idiomatique», alors ce quelque chose correspond à un certain style d'un certain temps. Si quelque chose n'est pas idiomatique, cela ne correspond pas au style dominant. Ce n'est pas à la mode.

Plus important encore, lorsque nous disons que le code de quelqu'un n'est pas idiomatique, cela n'explique pas la raison. Pourquoi pas idiomatique? La réponse est donnée par le dictionnaire.

Idiome (n.): Une révolution de la parole, utilisée dans son ensemble, non soumise à une décomposition supplémentaire et ne permettant généralement pas de permutations en elle-même.

Les idiomes sont la marque des significations courantes. Les livres ne vous apprendront pas le Go idiomatique; il n'est connu que lorsque vous faites partie d'une communauté.

Je suis préoccupé par le mantra idiomatique de Go, car il est souvent restrictif. Elle dit: "tu ne peux pas t'asseoir avec nous." N'est-ce pas ce que nous voulons dire lorsque nous critiquons le travail de quelqu'un d'autre comme «non idiomatique»? Ils l'ont mal fait. Cela ne semble pas correct. Ce n'est pas conforme au style de l'époque.

Je crois que le Go idiomatique n'est pas adapté pour enseigner comment écrire un bon code, car, essentiellement, cela signifie dire aux gens qu'ils ont fait quelque chose de mal. Il vaut mieux donner de tels conseils qui ne repousseront pas une personne au moment où elle souhaite le plus recevoir ces conseils.

Énonciations


Détournons-nous des problèmes idiomatiques. Quels autres artefacts culturels sont inhérents aux programmeurs Go? Rendez- vous sur la belle page Go Proverbs . Ces paroles sont-elles un outil d'apprentissage approprié? Dit-il aux débutants comment écrire du bon code Go?

Je ne pense pas. Je ne veux pas minimiser le travail de l'auteur. Les paroles qu'il a composées ne sont que des observations et non des définitions de sens. Le dictionnaire vient à la rescousse:

Proverbe (n.): Une courte déclaration qui a une signification littérale ou figurative.

La mission de Go Proverbs est de montrer l'essence profonde de l'architecture du langage. Mais sera-t-il utile de conseiller comme "L' interface vide ne dit rien " à un débutant venu d'une langue sans typage structurel?

Dans une communauté en pleine croissance, il est important de reconnaître que le nombre d'élèves de Go est bien supérieur au nombre de ceux qui parlent couramment cette langue. Autrement dit, les dictons ne sont probablement pas la meilleure façon d'apprendre dans une telle situation.

Valeurs de conception


Dan Liu a trouvé une ancienne présentation de Mark Lukowski sur la culture de conception dans l'équipe de développement Windows Windows NT-Windows 2000. J'ai mentionné cela parce que Lukowski décrit la culture comme un moyen courant d'évaluer les architectures et de faire des compromis.


L'idée principale est de prendre des décisions basées sur la valeur dans une architecture inconnue . L'équipe NT avait ces valeurs: portabilité, fiabilité, sécurité et extensibilité. En termes simples, les valeurs de conception sont un moyen de résoudre les problèmes.

Go Values


Quelles sont les valeurs explicites de Go? Quels sont les concepts ou philosophies clés qui déterminent la façon dont les programmeurs Go interprètent le monde? Comment sont-ils proclamés? Comment sont-ils enseignés? Comment sont-ils suivis? Comment changent-ils au fil du temps?

Comment convertir un programmeur Go pour obtenir les valeurs de la conception Go? Ou comment, en tant que Go-pro expérimenté, proclamez-vous vos valeurs aux générations futures? Et pour que vous compreniez, ce processus de transfert de connaissances n'est pas optionnel? Sans l'afflux de nouveaux participants et de nouvelles idées, notre communauté devient myope et se flétrit.

Valeurs d'autres langues


Pour préparer la voie à ce que je veux dire, nous pouvons prêter attention à d'autres langues, à leurs valeurs de conception.

Par exemple, en C ++ et Rust, on pense qu'un programmeur ne devrait pas payer pour une fonctionnalité qu'il n'utilise pas . Si le programme n'utilise pas certaines fonctionnalités gourmandes en ressources de la langue, le programme ne peut pas être obligé de supporter le coût de maintenance de cette fonctionnalité. Cette valeur est projetée du langage dans la bibliothèque standard et est utilisée comme critère pour évaluer l'architecture de tous les programmes écrits en C ++.

Valeur principale en Java, Ruby et Smalltalk - tout est un objet. Ce principe sous-tend la conception du programme en termes de transfert de messages, de dissimulation d'informations et de polymorphisme. Les architectures conformes à un paradigme procédural ou fonctionnel sont considérées comme erronées dans ces langages. Ou, comme dirait un programmeur Go, pas idiomatique.

Revenons à notre communauté. Quelles valeurs de conception les programmeurs Go professent-ils? Les discussions sur ce sujet sont souvent fragmentées, il n'est donc pas facile de formuler un ensemble de significations. Il est impératif de parvenir à un accord, mais la difficulté d'y parvenir croît de façon exponentielle avec le nombre croissant de participants à la discussion. Et si quelqu'un faisait ce travail difficile pour nous?

Zen Python Go


Il y a quelques décennies, Tim Peters s'est assis et a écrit PEP-20 - Le Zen de Python . Il a essayé de documenter les valeurs de conception auxquelles Guido Van Rossum a adhéré en tant que dictateur Python généreux à vie.

Examinons Le Zen de Python et voyons si nous pouvons apprendre quelque chose sur les valeurs de conception du concepteur Go.

Un bon package commence par un bon nom


Commençons par le pointu:

Les espaces de noms sont une excellente idée, agrandissons-les!

Le Zen de Python, record 19.

Sans ambiguïté: les programmeurs Python devraient utiliser des espaces de noms. Beaucoup d'espaces.

Dans la terminologie Go, un espace de noms est un package. Il ne fait aucun doute que le regroupement favorise la conception et la réutilisation. Mais il peut y avoir confusion sur la façon de procéder, surtout si vous avez de nombreuses années d'expérience en programmation dans une autre langue.

Dans Go, chaque package doit être conçu pour quelque chose. Et le nom est le meilleur moyen de comprendre cette destination. En reformulant les pensées de Peteres, chaque package de Go devrait être conçu pour une chose.

L'idée n'est pas nouvelle, j'en ai déjà parlé . Mais pourquoi utiliser cette approche, et non une autre, dans laquelle les packages sont utilisés pour les besoins d'une classification détaillée? Tout tourne autour des changements.

— , .


Le changement est le nom du jeu auquel nous participons. En tant que programmeurs, nous gérons le changement. Si nous le faisons bien, nous l'appelons architecture. Et si c'est mauvais, nous l'appelons dette technique ou code hérité.

Si vous écrivez un programme qui fonctionne très bien une fois avec un ensemble fixe de données d'entrée, personne ne sera intéressé à savoir s'il a un bon code, car seul le résultat de son travail est important pour les entreprises.

Mais cela n'arrive pas . Il y a des bogues dans les programmes, les exigences et les changements de données d'entrée, et très peu de programmes sont écrits avec une seule attente d'exécution. C'est, votre programme va changer au fil du temps. Peut-être que cette tâche vous sera confiée, mais très probablement quelqu'un d'autre le fera. Quelqu'un doit accompagner ce code.

Comment pouvons-nous faciliter le changement de programme? Ajoutez des interfaces partout? Faites tout ce qui convient pour créer des talons? Déployer les dépendances étroitement? Peut-être que pour certains types de programmes, ces techniques conviennent, mais pas pour beaucoup. Cependant, pour la plupart des programmes, la création d'une architecture flexible est plus que de la conception.

Et si au lieu d'étendre les composants, nous les remplacerions? Si le composant ne fait pas ce qui est spécifié dans les instructions, il est temps de le changer.

Un bon package commence par le choix d'un bon nom. Considérez-le comme une courte présentation qui décrit la fonction d'un package avec un seul mot. Et lorsque le nom ne répond plus à l'exigence, trouvez un remplaçant.

La simplicité compte


Simple, c'est mieux que complexe.

Le Zen de Python, entrée 3.

PEP-20 prétend que le simple est meilleur que le complexe, et je suis entièrement d'accord. Il y a quelques années, j'ai écrit:


La plupart des langages de programmation essaient d'être simples au début, mais décident plus tard d'être puissants.

Selon mes observations, au moins à cette époque, je ne me souvenais pas d'une langue que je connaissais qui ne serait pas considérée comme simple. Comme justification et tentation, les auteurs de chaque nouvelle langue ont déclaré la simplicité. Mais j'ai trouvé que la simplicité n'était pas la valeur fondamentale de nombreuses langues du même âge que Go (Ruby, Swift, Elm, Go, NodeJS, Python, Rust). Peut-être que cela frappera un point sensible, mais peut-être la raison en est qu'aucune de ces langues n'est simple. Ou leurs auteurs ne les considéraient pas comme simples. La simplicité n'était pas incluse dans la liste des valeurs fondamentales.

Vous pouvez me considérer comme démodé, mais quand cette simplicité est-elle passée de mode? Pourquoi l'industrie du logiciel commercial oublie-t-elle constamment et joyeusement cette vérité fondamentale?

Il y a deux façons de créer une architecture logicielle: pour la rendre si simple que l'absence de défauts est évidente, et pour la rendre si complexe qu'elle n'a pas de défauts évidents. La première méthode est beaucoup plus difficile.

Charles Hoar, The Old Emperor's Old Clothes, Turing Award Lecture, 1980

Simple ne signifie pas facile, nous le savons. Souvent, il faut plus d'efforts pour garantir la facilité d'utilisation plutôt que la facilité de création.

La simplicité est la clé de la fiabilité.

Edsger Dijkstra, EWD498, 18 juin 1975

Pourquoi viser la simplicité? Pourquoi est-il important que les programmes Go soient simples? Simple signifie brut, cela signifie lisible et facile à suivre. Simple ne signifie pas sans art, il signifie fiable, intelligible et compréhensible.

Le cœur de la programmation est la gestion de la complexité.

Brian Kernigan, Outils logiciels (1976)

Que Python suive son mantra de simplicité est une question discutable. Chez Go, cependant, la simplicité est une valeur fondamentale. Je pense que nous conviendrons tous que dans Go, le code simple est préférable au code intelligent.

Évitez les états au niveau du package


Explicite vaut mieux qu'implicite.

Le Zen de Python, entrée 2

Ici, Peters, à mon avis, rêve plutôt qu'adhère aux faits. En Python, beaucoup n'est pas explicite: décorateurs, méthodes de dunder, etc. Sans aucun doute, ce sont des outils puissants, et ils existent pour une raison. Sur la mise en œuvre de chaque fonctionnalité, particulièrement complexe, quelqu'un a travaillé. Mais l'utilisation active de telles fonctionnalités rend difficile l'évaluation du coût de l'opération lors de la lecture du code.

Heureusement, nous, les programmeurs de Go, pouvons éventuellement rendre le code explicite. Peut-être que pour vous, manifestation peut être synonyme de bureaucratie et de verbosité, mais c'est une interprétation superficielle. Ce sera une erreur de se concentrer uniquement sur la syntaxe, de prendre soin de la longueur des lignes et de l'application des principes DRY aux expressions. Il me semble plus important de fournir une explication en termes de connectivité et d'états.

La connectivité est une mesure de la dépendance de l'un à l'autre. Si l'un est étroitement lié à l'autre, alors les deux se déplacent ensemble. Une action affectant l'un se reflète directement dans l'autre. Imaginez un train dans lequel toutes les voitures sont connectées - ou plutôt connectées - ensemble. Là où va le train à vapeur, il y a les voitures.

La connectivité peut également être décrite par le terme cohésion - cohésion. C'est une mesure de combien l'un appartient à l'autre. Dans une équipe soudée, tous les participants sont tellement adaptés les uns aux autres, comme s'ils avaient été spécialement créés ainsi.

Pourquoi la cohérence est-elle importante? Comme dans le cas du train, lorsque vous devez changer un morceau de code, vous devez changer le reste du code étroitement lié. Par exemple, quelqu'un a publié une nouvelle version de son API et maintenant votre code ne se compile plus.

Une API est une source inévitable de liaison. Mais il peut être présenté sous des formes plus insidieuses. Tout le monde sait que si la signature de l'API a changé, les données transférées vers et depuis l'API changeront également. Tout dépend de la signature de la fonction: je prends les valeurs d'un type et renvoie les valeurs d'autres types. Et si l'API commence à transférer des données d'une manière différente? Que faire si le résultat de chaque appel API dépend de l'appel précédent, même si vous n'avez pas modifié vos paramètres?

C'est ce qu'on appelle l'État, et la gestion de l'État est un problème en informatique.

package counter

var count int

func Increment(n int) int {
        count += n
        return count
}

Ici, nous avons un package simple counter. Pour changer le compteur, vous pouvez appeler Increment, vous pouvez même récupérer la valeur si vous incrémentez avec une valeur nulle.

Disons que vous devez tester ce code. Comment réinitialiser le compteur après chaque test? Et si vous souhaitez exécuter des tests en parallèle, comment procéder? Et supposez que vous vouliez utiliser plusieurs compteurs dans le programme, réussirez-vous?

Bien sûr que non. De toute évidence, la solution consiste à encapsuler la variable variabledans le type.

package counter

type Counter struct {
        count int
}

func (c *Counter) Increment(n int) int {
        c.count += n
        return c.count
}

Imaginez maintenant que le problème décrit ne se limite pas aux compteurs; il affecte également la logique métier principale de vos applications. Pouvez-vous le tester isolément? Pouvez-vous tester en parallèle? Pouvez-vous utiliser plusieurs instances en même temps? Si la réponse est non pour toutes les questions, la raison en est l'état au niveau du paquet.

Évitez ces conditions. Réduisez la connectivité et le nombre d'actions distantes cauchemardesques en fournissant aux types les dépendances dont ils ont besoin en tant que champs, plutôt qu'en utilisant des variables de package.

Faites des plans pour l'échec, pas pour le succès


Ne passez jamais les bugs en silence.

Le Zen de Python, entrée 10

Cela est dit à propos des langues qui encouragent la gestion des exceptions de style samouraï: revenez avec une victoire ou ne revenez pas du tout. Dans les langues basées sur des exceptions, les fonctions ne renvoient que des résultats valides. Si la fonction ne peut pas le faire, le flux de contrôle va d'une manière complètement différente.

De toute évidence, les exceptions non vérifiées sont un modèle de programmation dangereux. Comment pouvez-vous écrire du code fiable en présence d'erreurs si vous ne savez pas quelles expressions peuvent lever une exception? Java essaie de réduire les risques avec le concept d'exceptions vérifiées. Et pour autant que je sache, dans d'autres langues populaires, il n'y a pas d'analogues de cette solution. Il existe des exceptions dans de nombreux langages, et partout sauf Java, elles ne sont pas vérifiées.

De toute évidence, Go a pris un chemin différent. Les programmeurs Go estiment que les programmes fiables sont constitués de parties qui gèrent les échecs avant de traiter les chemins réussis. Étant donné que le langage a été créé pour le développement de serveurs, la création de programmes multithreads, ainsi que des programmes qui traitent les données entrant sur le réseau, les programmeurs devraient se concentrer sur le traitement des données inattendues et endommagées, des délais d'expiration et des échecs de connexion. Bien sûr, s'ils veulent fabriquer des produits fiables.

Je crois que les erreurs doivent être traitées explicitement, cela devrait être la valeur principale du langage.

Peter Burgon, GoTime # 91

Je rejoins les propos de Peter, ils ont servi d'impulsion à la rédaction de cet article. Je crois que Go doit son succès au traitement explicite des erreurs. Les programmeurs pensent principalement aux plantages possibles. Premièrement, nous résolvons des problèmes comme «et si». Le résultat est des programmes dans lesquels les pannes sont gérées au stade de l'écriture du code, et non comme elles se produisent pendant le fonctionnement.

La verbosité de ce code

if err != nil {
    return err
}

L'emporte sur l'importance de gérer délibérément chaque état défaillant au moment où il se produit. La clé de ceci est la valeur de la gestion explicite de chaque erreur.

Mieux vaut rentrer tôt que d'investir profondément


La fratrie est meilleure que l'emboîtement

Le Zen de Python, entrée 5

Ce sage conseil vient d'un langage dans lequel l'indentation est la principale forme de flux de contrôle. Comment interprétons-nous cette astuce dans la terminologie Go? gofmt gère la totalité de l'espace vide dans les programmes Go, nous n'avons donc rien à faire ici.

J'ai écrit ci-dessus sur les noms de paquets. Il est peut-être conseillé d'éviter une hiérarchie complexe de packages. D'après mon expérience, plus un programmeur essaie de séparer et de classer une base de code sur Go, plus le risque d'importation cyclique de packages est élevé.

Je crois que la meilleure utilisation de la cinquième entrée de The Zen of Python est de créer un flux de contrôle à l' intérieur d'une fonction. En d'autres termes, évitez un flux de contrôle qui nécessite une indentation à plusieurs niveaux.

La visibilité directe est une ligne droite le long de laquelle la vue n'est masquée par rien.

May Ryer, Code: Alignez le chemin heureux sur le bord gauche

May Ryer décrit cette idée comme une programmation en ligne directe:

  • Utilisez les instructions de contrôle pour revenir plus tôt si la condition préalable n'est pas remplie.
  • Placer l'instruction de retour réussi à la fin de la fonction, et non à l'intérieur du bloc conditionnel.
  • Réduisez le niveau d'imbrication global en extrayant des fonctions et des méthodes.

Essayez de vous assurer que les fonctions importantes ne se déplacent jamais hors de la vue vers le bord droit de l'écran. Ce principe a un effet secondaire: vous éviterez les conflits sans signification avec l'équipe sur la longueur des lignes.

Chaque fois que vous indenter, vous ajoutez une condition supplémentaire aux chefs de programmeurs, occupant l'un de leurs 7 ± 2 emplacements de mémoire à court terme. Au lieu d'approfondir l'imbrication, essayez de garder le chemin d'accès réussi de la fonction aussi près que possible du côté gauche de l'écran.

Si vous pensez que quelque chose tourne lentement, alors prouvez-le avec une référence


Renoncez à la tentation de deviner face à l'ambiguïté.

Le Zen de Python 12

La programmation est basée sur les mathématiques et la logique. Ces deux concepts utilisent rarement l'élément de chance. Mais nous, en tant que programmeurs, faisons chaque jour de nombreuses hypothèses. Que fait cette variable? Que fait cette option? Que se passe-t-il si je passe à zéro ici? Que se passe-t-il si j'appelle le registre deux fois? Dans la programmation moderne, vous devez assumer beaucoup, surtout lorsque vous utilisez les bibliothèques d'autres personnes.

L'API doit être facile à utiliser et difficile à utiliser de manière incorrecte.

Josh Bloch

L'une des meilleures façons que j'ai connues pour aider un programmeur à éviter de deviner lors de la création d'une API est de se concentrer sur les méthodes d'utilisation standard . L'appelant doit pouvoir effectuer les opérations normales aussi facilement que possible. Cependant, avant d'avoir beaucoup écrit et parlé de la conception de l'API, voici donc mon interprétation de l'enregistrement 12: ne devinez pas le sujet des performances .

Malgré votre attitude envers les conseils de Knut, l’une des raisons du succès de Go est l’efficacité de son exécution. Des programmes efficaces peuvent être écrits dans cette langue, et grâce à cela, les gens serontchoisissez aller. Il existe de nombreuses idées fausses liées aux performances. Par conséquent, lorsque vous cherchez des moyens d'améliorer les performances du code, ou suivez des conseils dogmatiques tels que «le ralentissement des étagères», «CGO est cher» ou «utilisez toujours des opérations atomiques au lieu de mutex», ne faites pas de la bonne aventure.

Ne compliquez pas votre code en raison de dogmes obsolètes. Et si vous pensez que quelque chose fonctionne lentement, assurez-vous d'abord de cela à l'aide d'une référence. Go propose d'excellents outils d'analyse comparative et de profilage gratuits. Utilisez-les pour trouver des goulots d'étranglement dans les performances de votre code.

Avant de commencer la gorutine, découvrez quand elle s'arrêtera


Je pense que j'ai énuméré les éléments précieux de PEP-20 et peut-être élargi leur interprétation au-delà du bon goût. C'est bien, car bien qu'il s'agisse d'un dispositif rhétorique utile, nous parlons toujours de deux langues différentes.

Écrivez g, o, un espace, puis un appel de fonction. Trois pressions de bouton, il ne peut pas être plus court. Trois clics sur le bouton et vous avez lancé le sous-processus.

Rob Pike, Simplicity is Complicated , dotGo 2015

Les deux prochains conseils que je consacre aux goroutins. Les gorutins sont une caractéristique de la langue, notre réponse à une compétitivité de haut niveau. Ils sont très faciles à utiliser: mettez un mot godevant l'opérateur et vous exécutez la fonction de manière asynchrone. Aucun thread d'exécution, aucun exécuteur de pool, aucun ID, aucun suivi de l'état d'achèvement.

Les gorutins sont bon marché. En raison de la capacité de l'environnement d'exécution à multiplexer les goroutines dans un petit nombre de threads d'exécution (que vous n'avez pas besoin de gérer), vous pouvez facilement créer des centaines de milliers ou des millions de goroutines. Cela vous permet de créer des architectures qui ne seraient pas pratiques lors de l'utilisation d'autres modèles concurrents, sous la forme de threads d'exécution ou de rappels d'événements.

Mais peu importe le prix des goroutines, ils ne sont pas gratuits. Leur pile prend au moins quelques kilo-octets. Et quand vous avez des millions de goroutines, cela devient perceptible. Je ne veux pas dire que vous n'avez pas besoin d'utiliser des millions de goroutines, si l'architecture vous y pousse. Mais si vous l'utilisez, il est extrêmement important de les surveiller, car en de telles quantités, les goroutins peuvent consommer beaucoup de ressources.

Les goroutines sont la principale source de propriété de Go. Pour être utile, la goroutine doit faire quelque chose. Autrement dit, il contient presque toujours un lien vers une ressource, c'est-à-dire des informations de propriété: verrou, connexion réseau, tampon de données envoyant la fin du canal. Pendant que goroutine est en vie, le verrou est maintenu, la connexion reste ouverte, le tampon est enregistré et les destinataires du canal attendent de nouvelles données.

Le moyen le plus simple de libérer des ressources est de les relier au cycle de vie du goroutin. Une fois terminé, les ressources sont libérées. Et comme il est très facile d'exécuter goroutine, avant d'écrire «allez et espace», assurez-vous d'avoir les réponses à ces questions:

  • Dans quelles conditions la goroutine s'arrête-t-elle? Go ne peut pas dire à goroutine de finir. Pour une raison précise, il n'y a pas de fonction pour arrêter ou interrompre. Nous ne pouvons pas ordonner l'arrêt des goroutines, mais nous pouvons demander poliment. Ceci est presque toujours lié au fonctionnement du canal. Lorsqu'il est fermé, la plage est bouclée pour quitter le canal. Lorsque vous fermez la chaîne, vous pouvez la sélectionner. Le signal d'un goroutine à l'autre s'exprime mieux comme un canal fermé.
  • ? , , : ?
  • , ? , - . , . . , .


Probablement dans l'un de vos programmes Go sérieux, la simultanéité est utilisée. Cela conduit souvent au problème d'un modèle de travailleur - un goroutine par connexion.

Un excellent exemple est net / http. Il est assez simple d'arrêter le serveur propriétaire du socket d'écoute, mais qu'en est-il des goroutines générées par ce socket? net / http fournit un objet contextuel au sein de l'objet de requête qui peut être utilisé pour indiquer à l'auditeur que la requête doit être annulée, et donc interrompre le goroutine. Mais il n'est pas clair comment savoir quand tout cela doit être fait. C'est une chose à appeler context.Cancel, une autre à savoir que l'annulation est terminée.

Je trouve souvent des défauts avec net / http, mais pas parce que c'est mauvais. Au contraire, c'est l'API la plus réussie, la plus ancienne et la plus populaire de la base de code Go. Par conséquent, son architecture, son évolution et ses défauts sont soigneusement analysés. Considérez cette flatterie, pas la critique.

Donc, je veux apporter net / http comme contre-exemple de bonnes pratiques. Étant donné que chaque connexion est traitée par le goroutin créé à l'intérieur du type net/http.Server, le programme en dehors du package net / http ne peut pas contrôler les goroutins créés par le socket de réception.

Ce domaine de l'architecture est toujours en développement. Vous pouvez rappeler run.Grouple go-kit, ou ErrGroup, de l'équipe de développement Go, qui fournit un cadre pour l'exécution, l'annulation et l'attente de fonctions exécutées de manière asynchrone.

Pour tous ceux qui écrivent du code qui peut être exécuté de manière asynchrone, le principe principal de la création d'architectures est que la responsabilité de l'exécution des goroutines devrait être transférée à l'appelant. Laissez-le choisir comment il veut s'exécuter, suivre et attendre la fin de vos fonctions.

Ecrire des tests pour bloquer le comportement de votre API de package


Vous avez peut-être espéré que dans cet article, je ne mentionnerai pas les tests. Désolé, une autre fois.

Vos tests sont un accord sur ce que fait et ne fait pas votre programme. Les tests unitaires doivent bloquer le comportement de leurs API au niveau du package. Les tests décrivent sous forme de code ce que le package promet de faire. S'il y a un test unitaire pour chaque conversion d'entrée, alors vous, sous forme de code et non de documentation, avez défini un accord sur ce que fera le code.

L'approbation de cet accord est aussi simple que la rédaction d'un test. À tout moment, vous pouvez affirmer avec un haut degré de confiance que le comportement sur lequel les gens se sont appuyés avant les modifications que vous avez apportées continuera de fonctionner après les modifications.

Teste le comportement de l'API de blocage. Toute modification qui ajoute, modifie ou supprime l'API publique doit inclure des modifications dans les tests.

La modération est une vertu


Go est un langage simple avec seulement 25 mots clés. D'une certaine manière, cela met en évidence les fonctionnalités intégrées dans le langage. Ce sont les fonctionnalités qui permettent au langage de se promouvoir: compétition simple, typage structurel, etc.

Je pense que nous sommes tous confus en essayant d'utiliser toutes les fonctionnalités de Go à la fois. Combien d'entre vous étaient si inspirés par l'utilisation des chaînes que vous les avez utilisées partout où vous le pouvez? J'ai découvert que les programmes résultants sont difficiles à tester, ils sont fragiles et trop complexes. Et vous?

J'ai eu la même expérience avec les goroutines. En essayant de diviser le travail en petits fragments, j'ai créé l'obscurité du goroutin, qui était difficile à contrôler, et j'ai complètement perdu de vue le fait que la plupart d'entre eux étaient toujours bloqués en raison de l'attente de leurs prédécesseurs de terminer le travail. Le code était complètement cohérent, et j'ai dû augmenter considérablement la complexité afin d'obtenir un petit avantage. Combien d'entre vous ont rencontré cela?

J'ai eu la même chose avec l'intégration. Au début, je l'ai confondu avec l'héritage. Puis il a rencontré le problème d'une classe de base fragile, combinant plusieurs types complexes qui avaient déjà plusieurs tâches en des types énormes encore plus complexes.

C'est peut-être le conseil le moins efficace, mais je considère qu'il est important de le mentionner. Le conseil est le même: gardez la modération, et les capacités de Go ne font pas exception. Dans la mesure du possible, n'utilisez pas de goroutines, de canaux, de structures d'intégration, de fonctions anonymes, de nombreux packages et interfaces. Utilisez des solutions plus simples que les solutions intelligentes.

Facilité d'entretien


Enfin, je vais vous donner une autre entrée de PEP-20:

La lisibilité est importante.

Le zen de Python, fiche 7

On a beaucoup parlé de l'importance de la lisibilité du code dans tous les langages de programmation. Ceux qui font la promotion de Go utilisent des mots tels que simplicité, lisibilité, clarté, productivité. Mais tous ces éléments sont synonymes d'un concept: la commodité de la maintenance.

Le véritable objectif est de créer du code facile à maintenir. Le code qui survit à l'auteur. Un code qui peut exister non seulement comme un investissement de temps, mais comme une base pour obtenir une valeur future. Cela ne signifie pas que la lisibilité n'est pas importante, juste la commodité de la maintenance est plus importante .

Go n'est pas une de ces langues optimisées pour les programmes sur une seule ligne. Et pas une de ces langues optimisées pour les programmes avec un nombre minimum de lignes. Nous n'optimisons pas pour la taille du code source sur le disque, ni pour la vitesse d'écriture des programmes dans l'éditeur. Nous voulons optimiser notre code afin qu'il devienne plus compréhensible pour les lecteurs. Car ce sont eux qui devront l'accompagner.

Si vous écrivez un programme pour vous-même, il ne sera peut-être lancé qu'une seule fois, ou vous êtes le seul à voir son code. Dans ce cas, faites n'importe quoi. Mais si plus d'une personne travaille sur le code, ou s'il sera utilisé pendant une longue période et que les exigences, les capacités ou l'exécution peuvent changer, alors le programme devrait être pratique à maintenir. Si le logiciel ne peut pas être maintenu, il ne peut pas être réécrit. Et c'est peut-être la dernière fois que votre entreprise investit dans Go.

Ce sur quoi vous travaillez dur sera pratique à accompagner après votre départ? Comment pouvez-vous faciliter la maintenance de votre code pour ceux qui vous succèdent aujourd'hui?

All Articles