From 862d2d01f6e0b8287e88e02912ef4aba21328192 Mon Sep 17 00:00:00 2001 From: Fred Pauchet Date: Sun, 12 Jun 2022 17:09:19 +0200 Subject: [PATCH] Rework introduction --- chapters/api.tex | 50 ++++++++++++--- chapters/python.tex | 6 +- images/diagrams/basic-automation.drawio.png | Bin 0 -> 17318 bytes .../diagrams/deploy-without-hassle.drawio.png | Bin 0 -> 17609 bytes .../~$deploy-without-hassle.drawio.png.dtmp | 1 + parts/environment.tex | 60 ++++++++++-------- 6 files changed, 79 insertions(+), 38 deletions(-) create mode 100644 images/diagrams/basic-automation.drawio.png create mode 100644 images/diagrams/deploy-without-hassle.drawio.png create mode 100644 images/diagrams/~$deploy-without-hassle.drawio.png.dtmp 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 0000000000000000000000000000000000000000..9576097a1a52c773e5502871b67b7c95d7544562 GIT binary patch literal 17318 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 z
I#(Yddi}Gef^Zp~2rUI{p34lXLVI3I^?2rjO!~nrkDD<`Yn!2AuXbVF%BEyh# zIUzx>!mv;x47}pfaK+M)I= zduu*8Iy!2~&8GrS)&68CTJl)^ko*5-NUzwuViIC$&m>3Qj1`HPNxZ9a^wiGIu7X>4 zr`IQidsm;}o$~&|NdjFAhPgy96-m2eY*9Hr?hzSy>9tSKbjC3M2(_$z&KiOhN9Fwn zrYomc9hy<2#JYTWi|8S`MLY9Ek)Z6td87Xoka$y}XOw-Jk=e0>u)Mt(?$8$rPGaAH zgyb1NSOw|~%_CZ3mk<12m3aB6yot^4q+B3iAlZpM6ZTFB6s|0(lbsgn(IhQqO~3$# zkc!Ii12e4v5w~wojZsUQ&CG6rkks=(^8fE{LkxPKSxTETY>wAf z9?MDHciYY_KP(6NsQk%qV8>kw*?0K}ZW{@)dyAW_)YLK&o9G)uFR0eTdV!^hgd2jy zU`-eG|6DtA3hqn9nzzuu4}a1I;$*>5$`CfxXY%LH+da43q3-Cz@ei4U$)_X~8NlVb zg-L5?`?;WDQH)|GoP~VuZM8ryci^A)NfgQKYkWA#pd7rVL=TCH)9EM%8TgLQ=_nMH z#)?967Cv1$aA?-a0*}Ae4;#H#&~L<*|7kfeF5(^A4HcX z+N$72++Q4D-fh3n+o#;&VoVS%1EMb^aet3hE3l~!Ql%_u8am4rrq7B>lkt%hTynR0 z`}79gtbm4|=@UVSwfc%)zl2U@z+%OdYjh(0e+i0z{cZ?0soR6*>rLRZc|uUS+xXSn_C)PLv-W|F?4=oB;J`hz zd?%DC%Afc;X&YFNbm+xYx*XWXJtblzVV7bA*@X{Gl&7Ve-a!c*$91qX^(&|)*N?WO S%itex5b2{ZI!#(GiT^(8ab)k2bvVwkIrg51Y>rK`Lu4K#Dv>&woQ@X!#+DP`v74hE+VgD{ek8V;{Ew2Pan)9*2n1KTjOOg7-bVeK6B5=J66Fw46vcgrNbw6x37h^?-^LN; z{;!6__ys`&+{Qve93oKgq~YZ58vyd`9&O{#@8kih3yJfK@e6?>rTb_PH&97MN}OMi zUsPONlwU{)*Y*F*1Xo#*Ujmfr**Msso&Kvg+$2#xHje*HP)J+I5akbbu~JmiaJ6$V z(RGK4{4+5>6x!Pftjgat`gh;Bmwf^~QU8?Mqx_ugz(9mKL||ZT?VW5K(Kc?NiqpTA z&(qt@$kabb(@Ipu%hN`i&&F9v^`F+=ZNLovHmw6zkPl1?*NBASeJwjNZ-1DMppZTc z3|iY!)h)nI*xyb~Owi3%Uq?+9$sr6ic6JOv8z^b&!gSS?9Yh3OVTvYB0irJYp5i7! ze)`Hbo`Qx-w%~O;A0<%}DP;%Ep!>cWj&M+7qzH%U=m+|XNCZF~wVlNS)qG)Ij$W$D zYT_D-N)De{N}5*{#LXC;*&ls^*cY_H`i zWF(}lC!%i%*Ku=J^fB>Ob@mZ+boO`fQqj~3a1@b*N+>!A1$mkV8EOi{ybz{FYJx6G zVp=M$N)l@B_nn1YU3JvFRJ2X?Rn^ob+&mq;{Cu?BOtRY$>`4K%d%OhhDX;r1fxo{nPhKz(<0 zAv-r00}Yg>9$ZlzswnOo;H07BU>vx|$Q zuBoZBnzo>hn!C2Gr=yX-gQ_-66y||)l91A|Q}#l-Y8!x&x_aulYiRn|tJ;9(koN_B zG=L(21_s?%6mkfZ@^n^L4|MTF!#qKuzS4d0dEd<)>g@p4P!)9b6GwZwg4Y6dk*;oV z2MJegDQ6=MKO<*NKQCK5q=bqP0xjWVWGgPB4yr*7(Go(qr4#ot)o^k%5H$=G6ZAH4 zQnV3q6B71?*?Sq8dU+X}8hV-<*#`I+DXN>oeO>icprZFRkXleJ2}hKuGg8f2P*)o+ zEUb@G5tkG}I0xv10Z58z*nx&&UWWIDY=s3SaI%Lm^b#@_cQ-V^O$_Cu2Fjpben5dh zkxsbZ;o_b?{=)uXr&Ub6g3xw`rbjoiE>y}>J@nz-fz z#FbE@j@oDm36#EurlYZQpq{vvzXMR>Dz@UdL#3vLv=gz>@NyAR(a?3*b8vHXZ?6gsGSV=%^KtVBU%g?f`ar>} zC~4z*M?1SA#l5A#lfNcTX^8+ut@3Ye_gD3S@BgwaA~2z@x=uViW;|78Mg0KFjohm< z2A|I_I-&-Qv(U5LSuO5R_QLn{!se_NQBko-HOMuVENCjie*J_VIfK=qzuQ9J63cG% zNR+#qqu}CK+rx{qKUzukBi_S-!*h=cd)znf?w@hmQ3iNbwiRdcc|9SY6w(U}_&)$rOK)sd5r~XR*nY9MT{C zdS&D1ivxAV_Fvup$A3m0_C^s*pX?ZnNLZJfk`LbImddTIhv=KN)VL^)CF2c64&dt0vGl#}HE6){D++_ka6wSmJf5`nMZW0>53RW({h zOPaa8NzV;EKOS%Nm={yd5phh+^&XZIx9uTznR@M1&~u?AJLa@9VHNtO)~*V!v6%XZvOi3Z7hdEE+C=#N{MhNx@K=VJd+q5~$GoQL86)QTfyVs_tNF7Py*MaS^tzSrQY41uTj1^wgV?>z z)~%@&_#LP27_!F2M49S^!;7PS`GjaHk``|DmwsT6{mp_m-Y8|~%LX@?X=vm~ev;k& zATj^-N47`ab}sa=So(zfHE;Q=ATX$k7^}&*jJ@zDDDRWi60?y!*@<^fUrKe|3ukTP z`PQ*_U%l>U3Y|F%*=jrVOWWbhcD3co@r>AbGhunqciZFZN`9!j%z91a*~rD8q_I-n z?v&$iosk=HTzNIeyDQU^j`?nxRu7vi7N=ZZy&+R>2!$j`Vhnxo9keI%6kzt8Hb#c6*^Wv8b%VZMwm4+O3hkNKrQb!A_i+`NsqB zoi^+I#YZ?xbXDSmRRsOy$3gzqb=$Mk!^W+Si#h(czoFT3AuUy{n=O8yV6;-RI_GCw zp)$X+ygt>~C-NNIIQb-u#hi|1tGl*ZL3Q-f8x9fwBi*?cBS9m(z`MIC_fS zD+hMpkoc;~m20n_!CMQKbk2l9^OC75j zv)DRH@*hq;iJbMHpRI0L<7xD?`}1~hRDD?O1{-Rxp9VYA>^%!jZV5g5PSgq{4?10N z!{>cOlK4*nk{0*bmPvZY9GfJ$lhQQ&B(nHku(yp}NK(Y{n32^7mj^2^Du9gSAM9!Q z5Zh~FjcKp{p%-yz>TJ9JiM8>ueH~8>vfe?{&dLRdvR|5Cwz!+?#o3`iOf_mO1{(V1 z;&>#Kf&OCevvxtXP33*g4t0UHFN2a!qwK#vmn3~Bjne7Nzlzyu>^S>iQstQIo56-6 z4oH$c@C>ccD@m-6Vm96LpG0Lg&&!{#*C)o7Xk;xsbfE0VqDJkJ#e_vOg)ovv$73+{ z=BrP;wvvhB6At>G++KMYdb-irdQkJRkSBT^Ua(x5_#k$i2@`=o=WhD-5g9Tf2=KMepf|_YN201*Fla6zcKe zF#Jn1l6E8x^C5@6ZbBK8MEz#FV4<3~&xQm#qEdx-?c^3^m@Ew7OvkTF{0o(yt05gO z$7VmeudQLNfq8OUq9JI1jb7Q1KbkC?>;^403Avogm7s}gi`e@BT0)}!O>2aTym z+kVC=y>0I;JEYvvx5(Ko0%V_TB5!`kfk|{Z*x>tIWbWE)AwLUZX6BFXAVU*vU2mE) zMQ7AX?-(>-OzwUE0Nx_wAq_21y2tmcvR>=5SN+&K8=gLBd0s?2igmeh{^4{kinx#7 zk>Ph;)SF17M_#=LgNZVmKj*p^+JOwrLc@9)eRn*b7SELFmAviqmuo3cl-qIA-cpWH z;D(IkNPQ04`)PCAy{TZD%3fIAH0xeq-Ry(i4_7*CB$`L$PBDChx(~jrQz*Hnw?T|q zxbh#=P1BzLvbu0?3qJH~I^WH|@ENBxyf0Uz5z_RWWd$4EE#35jO!6Dv3V(m^6SKE< z#R=20l3yazAKfDlLj-CnSBO}SHoqdf_gg^*2=(2!oV-oUtyr$=PnV@h_o9li%>)O9 zSrwA^9(BIkVp$dF_%oUUA*tP7>~GcjZLyg9Jx9S<=3ZvYQRKV81FMUZ%#D^`)$ZS$ z&JLy>*-^a955OGLG<^3`w1b1VqzOk;g^9erN-TcVTp7-N;nJq;?69>OLBj7jA#8k> zVHdaXqEC34ozFgwTFu6*d!K?+8F6&->y>}rRZbmV7S4MI7dtGdw+wIND<~&^4y3T< zE=Lbh{h|?m=@v_OH@8~}A7U3SGU8MbRV!V?c>ytUyz$vi>JsW)iuA2sn%Auor#}l5C@DN1H?C2{^5#9wmW+Fn z$81>X;KPv9{=<}t=xlA`l?R;B7h{H3$I-R=aONX9H__2ol1HUduQSVXvD56ye_lI% zo}s@&OixB`#-7vDU(t1;G_|T_wIbZUn`QJLV^MPjV#gv<=Jl;F^Bd_WU)jTFwa;p5 zj(!h)upNizo(9D3+?e10-uj@d#U_+axNp&*x)`&kTP@A4-@${(lC>xGsx8DsGmr|< zh#1n`Jwi{}jl(TUwaW$fk8tR$LIc~BFZJ5=zu#&`4Q05KsRR!gC=ETnok@R4K2(eY zS80F2l`{6>6GzoFS%O52iDKPW#Iz}g456o8qsv6KYqfg%D0_D9Behe=-$4~uoy`ep#bv+|z`4`C@Iw#yd9R^mL#Fb+mp%-K3nsLI30!4oS25XH=A22b_BnYqMOC0gW8WQuJN&LrA z`_=PKXkPTKAnWf;Q9rKF4HbF+VCj=O{Aps%M&iE$S?%(p+`B?%(o*|aLeL^Z_-(jN z2Kwvn>4B>BpTy{Jo*^WwxFE3(4O?9zyhU4izjs4Qy-?{2R9Ip6h zwIpBqP|#h!+LdA>PIB;pPfi#q8w&C4r^bWlx~5nS!79@?YPVw~czAcKGUKjx+3ze| z;>1(qkuo|j?G|2$(o8O^?h&*QUPV6)Z(8_C3XQ-tTVDB25hM~D`61xJ&LEe?mFHm? zz50W1mSjU~Jze~jj0Ad7s0Zb0?!;NqtW*;CymTc4waXc1(olS*K5i0H`|hbCk3BZM z*3~}eTCPd+e)WdMRFAygYbn+5lArTGC`m@sou@Z8MMN_zW1n*J-VN}ArCipnL6F&F zqh#ZyeFA`*Yqe+p(rFvNO-G}iE%eO2GPQ}FSKUM!sj^wKQs1jrlyet(F5hAJ;hC6` zU9?J;IcoxjY-8QY~H3!cFgKx^;#V*cJD-7y(gD^J?lwC?PF@h zrehybhOhBU2$O2o!)0O1I>rm-PXzhQ-L(GagV`FQje>tnj$USjVQMjvJzRfsee!aQ zuA*H^Qqgn=p}>&OJ|(dgQCRy)Vhc9)Ez4>jkJFv*I!rMeig!oD=4XQ zHk%$_Ys(W)2AR`^$coF1OV^t>eqA;KsiGenbdi?4(6P<)ePsx2y7Hbrm6r^5|@B z^}cS}o?Z%hlp;)5bh{!UrG^&;9HhIldWQ9`Le!9z2+YL@%?BiByWpkUhwQ8SPoG9G zl$F)gIt~GFx|?t+cGr^ErIJ_f{*2{=FE=aMQm^+cw>x|fTi5tltiwz)kd!)z9L7+T z@E{*qcl%_ib)~Cj<;RKKkXlH!N7`>y3qyz?w=bznSr~G0el=H>8W2o(zs~9}Uc|uu zHWc`n7lI7J6p^no&<%?mnYAN_5i55G&ZD9q4^NVj-j=YjBi*_($McqVNs{x9xP3ke z#12x=e4}tT$_!_YL^~c zu?jq7FXGx5)gQFPG!iUCAp<+z52>!+kgWZ%yDnAVf=XSqraKLcymA9wt;Ojjr-PtB zq&qd#7}ia1_Fj=byn8ovo+LP`=|_!1#grbHZ_FiyGLG5(L_rJN!Kf4t-?~@u2BtdM za6XO%Or-3xk4Y|pfSPptJt`@ORGHgIhX}4niqnCn*V}9N0P5ZRX#3Ld^OG^#RyJ>X zf#!sefw&O=c1^AI#fBf*C@;oGp~*}#_qpi2lchF2XjY9Im)c2@5ihZrtHITgJDP$A zlJEK(=yyqv8@lVX6tUniKTq~wzz0HT}P zn!gpX_w?nF$8dZ#a+s%EaOl0pvfHUY{JU!;S(1tGRQF!DstfY1(APlPuD+ne!kVr- zA`S^MXRE=3`=V~fDh$M6n@K;T3^Ih{h3eoo5Tc3qT?l&mt~zOyGuLOrT-8li1bu}7 zKwSp>T20=`PskgxrZgoCfx>7`%yjLSr}NfHOgn6FTeHrXRE={zOXR6z#ODd#6P7?V<8jDA-RCC;bE=ue2||p1C(`T3uG`o1?j8gT z8d%CQB`WY9VulRxE5DeCDY3R^Hf`pfBr25+7_F-sE*9&nU@x#H>)S}v`^S84ycBJJ z9x;cmex>R+B|Yfhc~y4gvG#%=EH20I|IIDwtpB=~^5fR!aph+F$D{Dv6@}QJVt=s> zMOp8(b1|8RL0Y{vmNH$${^FG2G|IfAl%bS)WbN_!Gr6_G3hNvA|A*(oXiJe5Nxc<$ z_~$5<_*ItM;hwHzy#BqBzie4NT>bntYsk@y_%$`8A~vdaDNjI=KH{hNSk+sG`IS6g zNiWlS)ih4jXIRLWhh?MwAxHfAR#ORFg?4H*cm!kHYos zzW5*SZ1~Rv4I2$(x6&)*u0?A@#_F#?*d&FlnkV3v<|gkiO4NAmH;q!WN*UA(Yl0o_sReTFAQ~FYMI(rFM`FS(eP;(lvew5~+`F?Mz~p)TD2P1}Rv{+r)er3aj_V}%zc%jcHm}D4}$$K&BJlmT2c9}O~gAveu%NCvH@$sRsY_&oc*K4 zq*p0dC_*l02*lF`F5@^a-`uSUS0} zT#;lxJ$QI*iv@a?MqOF$1-W#?!?Tlv9fCzfD;f-t>WEjw0jFcea`{5r-wHtE@5 z!XWt$n~~5}W#;>Ja;WT#M1SHp(kSfB%RKh}Hi9Y40&m@PH6YL5U=(I+OC_k+B(pp{DyntI7n_ELlw8pbW&!XRS z(_xEmrtQ_G)IZ&!$V@!1Lp!SZy86WcK_a&o@9&SYEhr&)j_X2+|Js*9 zxA_!GapMQQ1uL#K!#}cY*Vt|!?`a$mN2Q{5E~l|=y7FG*lp>X)7){m5(tR2HzMUjG zkP^ylD9mzeFlK1g`NUXjB2SnleFXkt30B&}9Cyi3;5+@*^k;6S(!7&^CX*p}X<5b`qQ%{xw@)W@)2q?xrJ{^T|KQ_L-azgM2bUuf|XiewiSBDIRzywN{^a) zEt7@CeYdG&@<<9u*0>u0%}X|BXUG8|V*YMvuTZbXVS?w*%Bdrqi&5%id#9{j^Pp0U zk_)3$RPE}UHUd8bu5)Y1!8_XmK2Q4x_SqSvWY>MkPoF~9?#;zuSga39BaePI0is9p z`lH*FNC#UKk|PeUCo>Kcd0p@IrNAALo5Ku@8g6_ z=7;vPjwBYgt{TWXMNf2b6N0 zyk0wx@npgh^z)3hnp1`2!)o?Ivv`+ld_q~51KOz)qP~dcH-YuLQI&&XHNYmxpMNEl z{>d!y5J7btSMJM;$s>t2l$-Y`m@<@&hB3RSH`jb97oR5|Sb6b?d~zN&LOViWc;)n! z@h})b_gV;mzO!@u!NKG{fahX>*j=tQdfzURabx^?%;0_6Mm9rIt_r981i2ZD!5XcQev}Vv zf!rG@(Cp)z=3j@2BsLw;C8HUF_%XwetCFDmO8W!Iak`@m;-ypFv%ng(i;C^!Gxchkx}-!^?wD!y2*;sMZn>nNFryF|E^0Y@eT99d%NLWUD`BLLj5xh1 zd`~ijQ6-~^A8e)K5m#P7q57qPB_<7=n8jfhPmB25k8B$QlwuTzmuvQt()mzm3;)&8 z-rN}n`C~v#`p@Nl=`{j_yJ>p-up`>pYX2rFz6oHK>WW-Oflqw$f6T`J&+rH%uqBXz z|7}YYix_A}^BP|yO$l87CzPX}{YjlIL^;XJoDYO@pi{ekN2LaTpkXc!?KDqlFGLr2 z1GdTdLOCcQrxmwVISHc_dWZ+Q66mZA@xv zDvW%90zUn~}z|g_tmZyjV<| zUK}AM0hE~^RPth}@~eX>6y@+CgS3c=!|Rk3ip>3Z_>M{AFzS}kYhmQ%1jnq*$YENk zHd<=Xdug&k8yHDIQW#<~n3_XN0W_;$O-h2#Le9*L95?=b13#tC<5tQ&P^srp7(z! zMGRtH;0Z@*r3G~oRB@#l(>Vl4rGw)>|wC1x=V(?c12^;sg0rhpfJ z8&eF}9C4qYIxgdtCc|}dfR7aST2O8UmgDZ+#+R}*F{ja<7T;ePwe96ox#Dim06Au% za@#8tekZU;+UF;^f4#E+30Py3Fkpuiq}cEvTOdNyU_Y2z*?LuXo5_A9xa={5$e>jg zhL+dZnuL~Lbgi;nzht(2t$KL=;&lFE3ZIC<8bn6QwZwuXSV2=1_!Z943T4d6ahOmL zn3B2pv%3*VCRc-_p-r+S<^o=90M>VNC7!=!An0JdS;Z*u0PvYL1{H>rS8lsnlgaKS zyS*7R^gG+1m|hx8d(v%Syv(MOPy>eU|HK~<(|)(boG}|U-J}g5oOFxkxb1Mx#muAa z4WCu}jI4SH&b8{eSdqVQiXxn}@^)JiBH z(`8A%6RuHoy>sD54v6yw(|7e$odMu89mDkS={QpE=kv%)tB!|$fQ_HZ^`Bcs(D@5Q z5#w)*^wdmP22V<@gd9xvw;wH#z<(xhfs?(CBfw>k zm(mV^)oYPH4&Io0F?`vW&9gR1m1pplUqiZu@f)Q{L+iv_qpmK?b)F(b74706V^AQ-7Q~G$!+1L|yDuP;^;=r{F zX9hvYmbA59o)k)SdIBZ9k1hHabu4S64EDe!QjOw5;N_P;CAD<@JhvAd`P`S zPQ`U{t~!N>V50KXLB!;flU6TBl0}8}eE#@-adpPrdpXjcmO7g$KSM#&Krlks8^x@Ev95_aSYw ze$$gw+V^Z2W&C_%qL^=z>e}$xaV!omdo;Q0k!%{o?gHiLO^6Fk%cy6r()8~{MKRlwD%m*MeyU%&5%@CW%O#^p z>^nKWYCn7H+IJ(kSrL7cJ3q36_ZgXcMb}5@O-D0W1+8*8?$`accCp4{ATA7h1R|tC zpBzaaeJ>w=gz=y8g2zZ?h{pUfmb@Gv=2P_GN0wttFS)eh73vOha>!{hnJOD$)Vm8f z^DLtcQ|I>nT3V8^>dSIq;*Fl9iDP~5uu zZ%cSj5tz$3R`y50T>dRfsq4d8^}o;BptImY;y4!2*)w8{DJNT@A4Nu5)4cNCp$5;k z^|!mj-nI43&^~H`!tgS7DgYvc_p2>Q@Ly4Ik7=mPq;p=8ps26oi<)3hrT5Qnc6mSI zy)x`uplOgA18jewv{6#Df-X7nP@VEuRe~t;#jIEh;{p3Twamd-Jl1=-Ky#}cxkW-v zN=dHYGZIPYOOA*D^2ezPtcvEYd77EqkID0*%9bo_mGolqwcg&hHMR;|PUp`MSCk_D zMQ{HeD^5c5T_t4$Bt>`?z^_iIGL}Vuod78gRY%lLqN)mt-zd|U07lTt8y>S`A(tVr zSGzX!laOVhi=%+R>zQgMqUe>+c9uwr@TWS2wIUC!D-`;k-IZ9)xbJRZEFd?bMwGcE z5uEy*DRp^)Kj>*$MW%U;laE2bz~25f>w7Me#~>shy`U{xpTY)jNzUO#>i^fw*NHXq zqKM!P8Cx3~^8641f>y~a0keSxYxiJ?O^1B4~ zNm$IMIAzkNM%5m?tK#l6l6(;DXQG29o#8kKmp$?w>^%zv&8c@JL(zF8@TO=efZD#` z+RQ}H+dgE-?@pqLflX!ExA`tKyu3?JpI;g*DRWjBpzc2HCG={m-Rwrl7)F=X7J}8} zzqRLVOw3M%zr$5BRJVuA25@g)rpY{{Eo60}Y>pcIf#FZ1kb1sf*&ceI={M zHZa7qcLX406?GUBIxtVz72Z(@y5_O;WFszJXvn@-QQ+Ytj0ra(QCr=Ih|7@OJEyZv zBa@k%v#p0rg8332c|$y~KK^ujHn6}A=VQSDQUcNXO$@*-9GlGjk8mZs0!bSj?+6mW z{YXx52dvEjf}RovfOG)GH&1%5a)YQa0Wj*2Pf<%iTxL!`9G;yqXo#{j4)LTi&b8p|9BH}T0*7D4*JjlBk<+meOZuUAd&!2K^y9< zvVaL;@x!w{+9x?W=yj_1K_vZm8*Z8phfFR{K?8i>zQ)wPr$^5n8V&^%q^tfH8Ol{ z2+~x{-UfITRxX7R-gFRsq?n1K`VYp(&=Qo5qmfniDRrTCrbX-dTI?x*8%TEM;~G<= zf%FT0M7u(Za@eec%)xH#DB`(feg2x$pfzQQeh%A!oQmbaun$+2MuFBOXCp8k!X>jj zxg#s>6+FngOh;r&su_pz##2wa24-wYd=I3v_tb>klSJq-id+eRoZkvY5>*~7BK|!y*6pCD3<;4eyIYoawHm` z$JWJSUML&+0!QuJ4T4EZ_lA+c&$Z6ikk^r<%6Zbh<@ZYVMYN=yosdu=E9h|86+23ZUgI z$RU&G>IU%IvEFgS&yh4)2+N90q_i0019C}AyrlNJS#Ar)wR^MhX;H!eE)h}o1ON3GneYx)z2j|BywwDoIuin{!q}R7|68V4S7|X`{Bj5ZQ9PA;B^}#%fR9% zO!9MnbAgVn>vcos0ujHQo9Zt`wEylRxj`#T-%i74YH#M=Tr3Mf%|TM=pKe?_Row#+ zYQ>s-y1BMUDowKm(ElbP1eXF8s(yAXe{q~7=05WkXyw*x69;p_TN~9wHXOTiJ#kdK z3KEA#%GhSRml8mBxhV+C)howBI&NC?h=ah9Jn-23fRiiba`SE$!SPOG<6O{Cim6wep;=WcW+YC{>s&k?Y`R$Aji@$8U0(t zO<{c6z11xm`|!^;9G3u-Ba^+td?EQ5Aw`NA!=aU0HXUv#pT2DErDRlOFZ(hslTS-( z`KIIkg7gn%aiR{MOjlLs|PzS^NseyuiK< zvc>ATfW;?nO9LsJ$+Y8M4MhoJ_dq7xG&tl8aSNM2$c+I_kWr2|QTD?*w7JC} zeEICdzQC|@PmBj~`dyWUoI0GQ#j={H4eZ&bsE|oHH~G69DeoCehk|QYC75Mn`7Pqo zFp3s zyHt?Sy-#p(JXR*b>TH_N1NAEC!>-<}!=}3rR9mtn;Wf>ibbMa>GmxT481)&XV+np+~X(_5QumVu_KmkT**3U1Xn znwiz)a7?MQS~UKTo)2W!;VyjNunK)m1gW1GgxG2^$pbf)y6C#@VtWX1cV@2IZ+>gv z!QR~@{9O=pXN|yxoOluN9G)5C{J%A#Wtss`ZPd9S1qX;h{IT4p>c0m^B_Xz8nlIx#rKcT5(g#9#IBa;+#4_-wOI6;N*=j0hK z{79O@Hhp9t>yLL-G4aF{feUe5Ef-{nta%D&${{+WgW|hcGejVc7Vvixq1L+N{Zlmq z&2P#-T+h0^@U7Y~8VH&oK{PYgRhdU$rmRS#)qFk+_`3CBURFvC$@?^Bt~UO2fh8n` ziazD$s#&rq^I3+2(LA*HE&fdr(8j{6aagrbn=KqJnArC6 z#U7_Q;E>OzLpI;1Dzg|N`43iIW_Ftv2T2Wn3Il>`WwYaOF4&@S?RoA8@#*(xa$-=U zb~iXAR60u;d+e;X1~Xu8x4QWhc_^vi@RUo(spi;&25q{gHAOt!svnVa?`Q))G%mr}1h7{OpQr-YW7!ftOQw!* zyL;8CY|HdrDr0-W^B)fMCS&*-rL*t6fHa;7F*z-S>Nh*C_sPG#4>SS@&0l5fxlsX& zr%5y)^apyP=bii9+h!o-{m>u!GL!ic8v0RV?Dt7J^>2yZ|F-!!O)a(SPAH&ha*Gps zHTp^c@iulAih&g|aI}=0{he*HK<$rH%TF>nFYvrlgi8jyN7-!S3^|vXt5`2=s~#Rq zI#MSwgzSCVd`wrpDu~PjWEPw0wGpN2KNBEj*8feyLjmKOcg-(A%HZ?FmI(idz5qv% ze?Po`2D}ZJGsowb2&sPj%6{CDk|jiQd^OJ(kX1g{UT8vOolV#5#innofB1RR7<<>S z(ld^g+xQ(hB$Na=N|_7%CF>m*=lll}z?1WXb+41sb2n-T+7NTCybbeR4R>G0LrYBnYNwRM<7jc4c$v`R%eI zhII%okrEKA-&jj2958LR!fMeeJ0^~~%QGLEnoO?$zIWQ!)XsAMAhWhXTkhEYHbAwl zT<3sFX{B3y-8m-0T6sAbJD_c>KgT3!tsq7|@~0djC4AqY7WCZCqC@@NaNmFaM2|Rd zfgMC1VzYQr=Q#7&{CNW8kqd2F%mZuS?jfJ=nEaXJJB8rUZio?k2KUT&;Ia5L&Do^B9lOl!<$)OUz6trCFDkD%n zYInlJVkep2u9a7~c12y?#JG<>=0$ag@*@w_nNn52QK6#U|u#r-(Tu1%Qg4&iLOXn*4TKWzPDg;7<=>RFO+4eHZVgcM2gUMyId<@kS!58dqJ zF(IF0mv9)?5O^7Cs#~Fzk$mxe|6;g7vsV8UcX+Uw{SQUYn(TvA3%amlbTtv>L=F?M z%i`?f@xO{gZcnnJ=0#Zpan1sDcu^X1^pxqB^-!Tu3Yvy_6+{FGv9$jbZ%q%-Ku%v$ z3<7FIp^}(A7xS~@u@!OUn`cq!JUEXmoDkP2KW%zu+8r2nS(s;x47`q3d7i_$niHe39_`*{-?f}_}Fbw9;L7VSP?F*<+U0xebDaFW59hHeL(R4 z{gHz(gU`&NEoi@PFpX;(=h9@mHDv9te(s-o?=4^9=ApBC*y8Y900gtHr?Rn_WN6>0cE`o?+xG}78h`-LclP(C<2a` zGpp1y`NbWF8Pwa-WPnfKda_pG1_BZd09Z}MQn2N~-U4XsGTZXzL$<_-X@jeiQVi)E zaG8RG0d~AlA4GF1XZmm83guS8n01M&KZ;VYRsvpXqCINam3-1thbH zfTJDEdQjmTu;Je0zn==(jsN@&UbDj`fiXgcko;%0fB^_$EGGhUV#f;ynd7*#zR^u4 z|N54gv;H34p;%1QOYQtk-eoYuq2^b(;07*(d1it`TwEE;@lr4j1b7sLDUJXeFgqtm z)aL$nTgCq*Xmh67Z@sp^CI9%a^hIJFaK@)`_lLm!f~Wgi^HxRXw%suBaV@P0*EMbU z@=)e9D$~M&ic9M?E_&cRQSEkivR_Z7CD|F?wRIf$s_mfe(T&?vAT`_V{LiuUPXnlu zN|8Afn_7z75a8O)0oQoQtYaxznW0RL+j!tNf-sW#+=;;!IGqS$f@RSx zE@hNctBF>Senu&KlKs8~F*`#z`q}%mN-TQx1wv|&%_M}D;3^zGVx5Gw>v)D#lUSGk)!g;JPC0Cy)y=Naeb)R( zDY!j$Tc~cZRwJk%Xmif%>7!fKh~_=t(mObSr$E?#aUNV@)>s|C4x&7KW-UHtV`X~% z4^LLPPC>%$>R$SRb{V6i+W zIJmEr_Wz0@8*syi)cDWB|LdeM;8&uw?7XK;g9nRc=|JTwdDXwXiD&S?op10Hhuj@- zLaG(wa6efQzXkjlF6fpG0MUnBnF-+TEFFXpkuv*J48ULW7s-X_VVGfxCC3|EoDKUGgJ{10vBCx{2B1qoXs{)?^Y49#&p3~3sK88W z-)3iN&|Ui!-0Si=e0d12XywdJbYB4a)8vIB?u;dK5dg=e4%ngjM`TRyAP6vApRp6T zkr%vKOTypuP9=fPCRf^*CqJ?GD^&_Q!MXy1>y0QJXZ@Qi0Elw|xutjW6AkLQaCb&< zm%4^?Lk2lA_3Im!+5mq4OKvg~mdD}I?BF5^X`GsMDo<`1)Hmz|hXAbO*FypS3i59F zM>ZL7##?FkV%~X}qvE zu9KtEe(R;OOT2vq!S6xjEUXP^A(MMma2$>0g{wn8K#5;1D+})54ZBsPG7>b^0;E;) z3pv@z0Z0-qSrE|4uS?b9h3tsfuU~(1%~O<QRmF|BqLO=}OI74vZAG7xako=&vGC$z$7{-R10}5i92@!a*w@Uq~k9qY= ze*7n*fh*1VfHF6G<7^no|ta^ULSZ3*|8TanO`S3|#Um(AQx zK?nXaYNH~6!QRCLV_}z{-+JPm?h0{rd&s0m<-3dflGm@^RatpP#d3zoMI2Vy@5kvOY^ zol0AWK367xD&_rh$WOW9C9vt~v0t|12h({yp8;48(ht}l;(xLI1lV6%i_4V2Bwh;F zeWB9KOa~M|dF-?FggnQIAz+_d*y%1^xn7gVDC2OLn#UspbR((+>5CvfipSf|#>Ecp z`V-T8a|$_mmq;0Fah!F{)2qQ!AhrsqEbsM=({bkDff?0y06^?n zPG9D*4zMBl34S^wiP;(P51s~t8)Nn^Ym@aQ6vua+IGPTWod>{WO4Z;2)!TS1XVefE zW}M*_C2Zo4mdSoA$sY49S?^y88K!w?d|v^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.