diff --git a/chapters/api.tex b/chapters/api.tex index 4328980..ee0d1e7 100644 --- a/chapters/api.tex +++ b/chapters/api.tex @@ -9,10 +9,12 @@ Expliquer pourquoi une API est intéressante/primordiale/la première chose à r \begin{itemize} \item Intéressante: ouverture - - \item Primordiale: services - \item La première chose à réaliser: mobile-first - \item Le cadet de nos soucis: monolithique (cf. Rework) + \item + Primordiale: services + \item + La première chose à réaliser: mobile-first + \item + Le cadet de nos soucis: monolithique (cf. Rework) \end{itemize} Voir peut-être aussi @@ -84,14 +86,11 @@ class Contract(models.Model): ) \end{minted} - - \includegraphics{images/rest/models.png} - \section{Mise en place} -La configuration des points de terminaison de notre API est relativement touffue. +La configuration des points de terminaison de notre API peut être relativement touffue. Pour cette raison, il convient de s'infliger à suivre une structure qui soit similaire pour chaque point de terminaison \cite[Predictability, Rule \#1]{django_for_startup_founders}. Il convient de: @@ -123,6 +122,8 @@ Il convient de: Et finalement ajouter quelques paramètres au niveau de notre application. \end{enumerate} +\section{Django Rest Framework} + \subsection{Serialiseurs} Les sérialiseurs agissent litérallement comme des \texttt{forms}, mais au niveau de l'API. @@ -231,7 +232,7 @@ En nous rendant sur l'URL \texttt{http://localhost:8000/api/v1}, nous obtiendron \includegraphics{images/rest/api-first-example.png} -\section{Modéles et relations} +\subsection{Modéles et relations} Plus haut, nous avons utilisé une relation de type \texttt{HyperlinkedModelSerializer}. C'est une bonne manière pour autoriser des relations entre vos instances à partir de l'API, mais il faut reconnaître que cela reste assez limité. Pour palier à ceci, il existe {[}plusieurs manières de représenter ces \url{https://www.django-rest-framework.org/api-guide/relations/}: @@ -322,3 +323,34 @@ class PeopleSerializer(serializers.HyperlinkedModelSerializer): Nous ne faisons donc bien que redéfinir la propriété \texttt{contract\_set} et indiquons qu'il s'agit à présent d'une instance de \texttt{ContractSerializer}, et qu'il est possible d'en avoir plusieurs. C'est tout. + +\section{Alternatives} + +Django-Rest-Framework est une librarie complète qui ajoute énormément de possibilités. +Cependant \cite{django_for_startup_founders}: + +\begin{enumerate} + \item + La documentation est \textbf{réellement} compliquée. + Tout nouveau développeur doit appréhender, comprendre et assimiler cette documentation et tous les concepts sous-jacents. + Ceci inclut notamment le fait que tous les verbes HTTP ont été "traduits" (GET -> retrieve, POST -> create, ...). + Ceci a du sens par rapport à la définition d'une interface REST-compliant, mais ajoute une complexité mentale relativement lourde. + \item + Certains concepts de réutilisation sont tellement compliqués qu'ils prennent plus de temps à mettre en place qu'à écrire une ligne de code Python classique + \item + Les sérialiseurs peuvent rapidement devenir difficiles à lire ou relire, spécifiquement lorsque nous utilisons des \textit{nested serializers} ou lorsque les concepts de désérialisation sont abordés. +\end{enumerate} + +\subsection{Marshmallow} + +\textit{Marshmallow} est une alternative plus légère à Django-Rest-Framework, et qui présente une interface plus claire, ainsi qu'une documentation plus concise et facile à comprendre. + +Une solution plus facile que les sérializeurs de DRF consistera à + +\begin{enumerate} + \item + Gérer la validation de données en utilisant Marshmallow + \item + Sérialiser les données en utilisant du code Python \cite{django_for_startup_founders}. +\end{enumerate} + diff --git a/chapters/python.tex b/chapters/python.tex index 9582c97..7155198 100644 --- a/chapters/python.tex +++ b/chapters/python.tex @@ -270,7 +270,7 @@ Les \href{https://google.github.io/styleguide/pyguide.html\#38-comments-and-docs L'exemple donné dans les guides de style de Google est celui-ci: \begin{listing}[!ht] - \begin{minted}{python} + \begin{minted}[tabsize=4]{python} def fetch_smalltable_rows( table_handle: smalltable.Table, keys: Sequence[Union[bytes, str]], @@ -284,9 +284,9 @@ L'exemple donné dans les guides de style de Google est celui-ci: Args: table_handle: An open smalltable.Table instance. keys: A sequence of strings representing the key of each table - row to fetch. String keys will be UTF-8 encoded. + row to fetch. String keys will be UTF-8 encoded. require_all_keys: Optional; If require_all_keys is True only - rows with values set for all keys will be returned. + rows with values set for all keys will be returned. Returns: A dict mapping keys to the corresponding table row data diff --git a/images/diagrams/basic-automation.drawio.png b/images/diagrams/basic-automation.drawio.png new file mode 100644 index 0000000..9576097 Binary files /dev/null and b/images/diagrams/basic-automation.drawio.png differ diff --git a/images/diagrams/deploy-without-hassle.drawio.png b/images/diagrams/deploy-without-hassle.drawio.png new file mode 100644 index 0000000..0fdfc62 Binary files /dev/null and b/images/diagrams/deploy-without-hassle.drawio.png differ diff --git a/images/diagrams/~$deploy-without-hassle.drawio.png.dtmp b/images/diagrams/~$deploy-without-hassle.drawio.png.dtmp new file mode 100644 index 0000000..6bbf257 --- /dev/null +++ b/images/diagrams/~$deploy-without-hassle.drawio.png.dtmp @@ -0,0 +1 @@ +7VhNc5swEP01TE/OgImxc2wcJ50mbTPJIR+XjgxrUCMQEQJDf30lEAYix4kTp6YzvTDap0VCb/ctC4Y9DfMzhuLgG/WAGEPTyw37xBgOj6yJuEqgqIDJ6LACfIa9CrIa4Br/BgWaCk2xB0nHkVNKOI67oEujCFzewRBjdNl1W1DS3TVGPmjAtYuIjt5gjwfqFMNxg38B7Af1zpZzVM2EqHZWJ0kC5NFlC7Jnhj1llPJqFOZTIJK7mpfqvtNnZlcPxiDir7mB3F+P6cXifPDz9jhPxoNJhr2BNVQPx4v6xOAJApRJGQ+oTyNEZg16zGgaeSCXNYXV+FxQGgvQEuAv4LxQ0UQppwIKeEjULOSY37bGd3Kpg5GyTnK1cmkULeMSGA6BA6uxiLPitm20VpJms1Rp1Wvp1Ck2E5oyFzbwpZKXI+YD3+Cnsl5y2dpABeYMqDgFK4QDA4I4zrrJhlTO+iu/JqxioCK7RZTVU2eIpGqnE2NqG5+P5inX4t+Kk4eSYBXnZYA5JDEq+VkKsQtM5HQszTD3pfoPvIV3kAhy+LMMZ8A45Bs5UbO20k3RNZctFSooaAmwdts5hyONw1Mc9Zc8q8ve0NwzfZalcdXXQvPXi8rklUWljmlPqspEU8RUvoRzTIUuHBTKHI/mSVxy4xBxsuM5EyNfjsr6I85jkk8QZZjJO0PJnObpgWwfIOE4Eoei79PcDqRlOU+lpSnLMtdIa/Rh0tLL+yzhA1fy9pjKKykprEp+TCh+hmnBsfBLI0lv6rql/yQRlmGf7lO9jWLvWjMvqbcR7F1Hr7tWb63Kl+Vr71q+6tZLimU4Vy50sUiAa3m12uEdqWZqqXZVpZWbxtWAKZGulTFB4hLRNBN7ge4jRJj0QOG22TeF683HjxT/85Lc+EqVh1b207b/HVq1X6nVUa/etJb+qv2+Rib/G6ptw2w5/YqzrcVZCzILaDhPkx1VOnvUqXTWmq8s+4M+Ex6Ce4gOr77eTLMgXjyS6NwfD/b6O+JNRc3aLqk3FLQnSfjG1He2qXB7S3RHS3T1RyJEjAHTW4OyhWWUkDlyH/bdIBzWLXctmw/sEITZ/COs+rjmR6s9+wM= \ No newline at end of file diff --git a/parts/environment.tex b/parts/environment.tex index 91f19b8..b1fc3fc 100644 --- a/parts/environment.tex +++ b/parts/environment.tex @@ -9,44 +9,52 @@ En fonction de vos connaissances et compétences, la création d'une nouvelle application est une étape relativement facile à mettre en place. Le code qui permet de faire tourner cette application peut ne pas être élégant, voire buggé jusqu'à la moëlle, il pourra fonctionner et faire "preuve de concept". -Les problèmes arriveront lorsqu'une nouvelle demande sera introduite, lorsqu'un bug sera découvert et devra être corrigé ou lorsqu'une dépendance cessera de fonctionner ou d'être disponible. -Or, une application qui n'évolue pas, meurt. -Toute application est donc destinée, soit à être modifiée, corrigée et suivie, soit à déperrir et à être délaissée par ses utilisateurs. -Et c'est juste cette maintenance qui est difficile. -Selon certaines études académiques et industrielles \cite[]{django_for_startup_founders}, 60 à 80\% du coût associé à n'importe quelle ligne de code correspond à de la maintenance de la ligne initialement écrite. +Les problèmes arriveront lorsqu'une nouvelle fonctionnalité vous sera demandée, lorsqu'un bug sera découvert et devra être corrigé ou lorsqu'une dépendance cessera de fonctionner ou d'être disponible. +Comme une application qui n'évolue pas finira inlassablement par mourir, toute application est destinée: + +\begin{itemize} + \item + Soit à être modifiée, corrigée et suivie + \item + Soit à déperrir et à être délaissée par ses utilisateurs. +\end{itemize} + +C'est cette maintenance qui est difficile: selon certaines études académiques et industrielles \cite{django_for_startup_founders}, 60 à 80\% du coût associé à n'importe quelle ligne de code correspond à de la maintenance de la ligne initialement écrite. Ceci est dû à des bogues, à des modifications de fonctionnalités, à des dépendances qui évoluent, qui ne sont plus maintenues ou qui doivent être remplacées. -L'application des principes présentés et agrégés ci-dessous permet surtout de préparer correctement tout ce qui pourra arriver, sans aller jusqu'au « \textbf{You Ain't Gonna Need It} » (ou \textbf{YAGNI\index{YAGNI}}), qui consiste à surcharger tout développement avec des fonctionnalités non demandées, juste « au cas ou ». -Pour paraphraser une partie de l'introduction du livre \emph{Clean Architecture} \cite{clean_architecture}: +L'application des principes présentés et agrégés ci-dessous permet surtout de préparer correctement tout ce qui pourra arriver, sans aller jusqu'à surcharger tout développement par des fonctionnalités non demandées, juste « au cas ou » \footnote{Aussi connu sous l'acronyme \textbf{YAGNI\index{YAGNI}}, ou \textit{\textbf{You Ain't Gonna Need It}}}. + +Pour paraphraser une partie de l'introduction du livre \emph{Clean Architecture} : \begin{quote} - Getting software right is hard: it takes knowledge and skills that most young programmers don't take the time to develop. + \textit{Getting software right is hard: it takes knowledge and skills that most young programmers don't take the time to develop. It requires a level of discipline and dedication that most programmers never dreamed they'd need. - Mostly, it takes a passion for the craft and the desire to be a professional. - - --- Robert C. Martin Clean Architecture + Mostly, it takes a passion for the craft and the desire to be a professional.} \cite{clean_architecture} \end{quote} Le développement d'un logiciel nécessite une rigueur d'exécution et des connaissances précises dans des domaines extrêmement variés. -Il nécessite également des intentions, des (bonnes) décisions et énormément d'attention. -Indépendamment de l'architecture que vous aurez choisie, des technologies que vous aurez patiemment évaluées et mises en place, une architecture et une solution peuvent être cassées en un instant, en même temps que tout ce que vous aurez construit, dès que vous en aurez détourné le regard. +Il nécessite également des intentions, des prises de décisions et énormément d'attention. +Indépendamment de l'architecture que vous aurez choisie et des technologies que vous aurez patiemment évaluées, une architecture et une solution peuvent être cassées en un instant, en même temps que tout ce que vous aurez construit, dès que vous en aurez détourné le regard. -Un des objectifs ici est de placer les barrières et les gardes-fous (ou plutôt, les "\textbf{garde-vous}"), afin de péréniser au maximum les acquis, stabiliser les bases de tous les environnements (du développement à la production) qui accueilliront notre application et fiabiliser ainsi chaque étape de communication. +Un des objectifs ici est de placer les barrières et les gardes-fous, afin de péréniser au maximum les acquis, stabiliser les bases de tous les environnements (du développement à la production) qui accueilliront notre application, et ainsi fiabiliser chaque étape de la communication. Dans cette partie-ci, nous parlerons de \textbf{méthodes de travail}, avec comme objectif d'éviter que l'application ne tourne que sur notre machine et que chaque déploiement ne soit une plaie à gérer. -Chaque mise à jour doit être réalisable de la manière la plus simple possible, et chaque étape doit être rendue la plus automatisée/automatisable possible. -Dans son plus simple élément, une application pourrait être mise à jour simplement en envoyant son code sur un dépôt centralisé: ce déclencheur doit démarrer une chaîne de vérification d'utilisabilité/fonctionnalités/débuggabilité/sécurité, pour immédiatement la mettre à disposition de nouveaux utilisateurs si toute la chaîne indique que tout est OK. -D'autres mécanismes fonctionnent également, mais au plus les actions nécessitent d'actions humaines, voire d'intervenants humains, au plus la probabilité qu'un problème survienne est grande. +Chaque mise à jour doit être réalisable de la manière la plus simple possible, et chaque étape doit être le plus possible automatisée. -Dans une version plus manuelle, cela pourrait se résumer à ces trois étapes (la dernière étant formellement facultative) : +Dans son plus simple élément, la mise à disposition d'une nouvelle version d'une application pourrait se résumer à ces trois étapes: -\begin{enumerate} -\item - Démarrer un script, -\item - Prévoir un rollback si cela plante (et si cela a planté, préparer un post-mortem de l'incident pour qu'il ne se produise plus) -\item - Se préparer une tisane en regardant nos flux RSS (pour peu que cette technologie existe encore\ldots). -\end{enumerate} +\begin{figure}[H] + \centering + \scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/diagrams/deploy-without-hassle.drawio.png}} +\end{figure} + +Dans une version plus automatisée, une application pourrait être mise à jour simplement en envoyant son code sur un dépôt centralisé: ce déclencheur a la responsabilité de démarrer une chaîne de vérification d'utilisabilité, de bon fonctionnement et de sécurité, pour immédiatement la mettre à disposition de nouveaux utilisateurs si chaque acteur de cette chaîne indique que tout est OK. + +\begin{figure}[H] + \centering + \scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/diagrams/basic-automation.drawio.png}} +\end{figure} + +D'autres mécanismes fonctionnent également, mais au plus les actions nécessitent d'actions humaines, voire d'intervenants humains, au plus la probabilité qu'un problème survienne est grande, même dans le cas de processus de routine. Sans aller jusqu'à demander de développer vos algorithmes sur douze pieds, la programmation reste un art régit par un ensemble de bonnes pratiques, par des règles à respecter et par la nécessité de travailler avec d'autres personnes qui ont souvent une expérience, des compétences ou une approche différente.