You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1163 lines
64 KiB
1163 lines
64 KiB
\documentclass[12pt, a4paper]{article}
|
|
|
|
\setlength{\parskip}{1em}
|
|
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[a4paper, margin=0.7in]{geometry}
|
|
\usepackage[french]{babel}
|
|
\usepackage{xspace} % From https://tex.stackexchange.com/questions/150640/missing-space-after-guillemets
|
|
\usepackage{amsmath}
|
|
\usepackage{amsfonts}
|
|
\usepackage{mathtools}
|
|
\usepackage{mathabx}
|
|
\usepackage{listings}
|
|
\usepackage[many]{tcolorbox}
|
|
\usepackage{xcolor}
|
|
\usepackage{float}
|
|
\usepackage{cite}
|
|
\usepackage{subcaption}
|
|
\usepackage{wrapfig}
|
|
\usepackage{svg}
|
|
\usepackage{ragged2e}
|
|
\usepackage[nottoc,numbib]{tocbibind}
|
|
\usepackage{array}
|
|
\usepackage{booktabs}
|
|
\usepackage{stmaryrd}
|
|
\usepackage{dsfont}
|
|
|
|
\usepackage{hyperref}
|
|
\hypersetup{
|
|
colorlinks=true,
|
|
linkcolor=blue,
|
|
urlcolor=blue,
|
|
citecolor=red,
|
|
pdftitle={Adrien Burgun - Rapport de stage ST40 - A2022}
|
|
}
|
|
|
|
% From https://github.com/pinam45/utbm-latex-internship-report-covers
|
|
% Requires xelatex to run (or maybe also lualatex)
|
|
\usepackage{utbmcovers}
|
|
|
|
\setmonofont[Contextuals={Alternate}]{Fira Code}
|
|
\newfontfamily{\FiraCode}[Contextuals={Alternate}]{Fira Code}
|
|
\IfFontExistsTF{Fira Code SemiBold}{
|
|
\newfontfamily{\FiraCodeSB}[UprightFont={* SemiBold}]{Fira Code}
|
|
}{
|
|
\IfFontExistsTF{Fira Code Semi-Bold}{
|
|
\newfontfamily{\FiraCodeSB}[UprightFont={* Semi-Bold}]{Fira Code}
|
|
}{
|
|
\newfontfamily{\FiraCodeSB}[UprightFont={* Semibold}]{Fira Code}
|
|
}
|
|
}
|
|
|
|
|
|
\definecolor{kwColor}{rgb}{0.15,0.21,0.87}
|
|
|
|
% From https://tex.stackexchange.com/questions/89574/language-option-supported-in-listings
|
|
\lstdefinelanguage{JavaScript}{
|
|
keywords={
|
|
typeof, new,
|
|
abstract, class, interface, type, function, return,
|
|
async, await,
|
|
try, catch, finally,
|
|
switch, case, break,
|
|
var, let, const,
|
|
if, else,
|
|
for, while, in, of, do,
|
|
true, false, null, undefined
|
|
},
|
|
keywordstyle=\color{kwColor}\FiraCodeSB,
|
|
ndkeywords={
|
|
export, implements, import, this,
|
|
private, public, readonly, constructor,
|
|
@Inject, @Injectable, @Controller, @Module
|
|
},
|
|
ndkeywordstyle=\color{darkgray}\FiraCodeSB,
|
|
identifierstyle=\color{black},
|
|
sensitive=false,
|
|
comment=[l]{//},
|
|
morecomment=[s]{/*}{*/},
|
|
commentstyle=\color{purple}\ttfamily,
|
|
stringstyle=\color{red}\ttfamily,
|
|
morestring=[b]',
|
|
morestring=[b]"
|
|
}
|
|
|
|
\lstdefinelanguage{hbs}{
|
|
% morecomment=[s]{\{\{!}{!\}\}},
|
|
morecomment=[s]{<!--}{-->},
|
|
commentstyle=\color{purple}\ttfamily,
|
|
stringstyle=\color{red}\ttfamily,
|
|
morestring=[s]{\{\{}{\}\}},
|
|
}
|
|
|
|
\definecolor{bgColor}{rgb}{0.95,0.94,0.92}
|
|
\lstset{
|
|
% backgroundcolor=\color{bgColor},
|
|
commentstyle=\color{gray},
|
|
keywordstyle=\color{magenta},
|
|
numberstyle=\tiny\color{gray},
|
|
stringstyle=\color{purple},
|
|
basicstyle=\footnotesize,
|
|
breakatwhitespace=false,
|
|
breaklines=true,
|
|
captionpos=b,
|
|
keepspaces=true,
|
|
numbers=left,
|
|
numbersep=5pt,
|
|
showspaces=false,
|
|
showstringspaces=false,
|
|
showtabs=false,
|
|
tabsize=2,
|
|
}
|
|
\tcolorboxenvironment{lstlisting}{
|
|
spartan,
|
|
frame empty,
|
|
boxsep=0mm,
|
|
left=1mm,right=1mm,top=-1mm,bottom=-1mm,
|
|
colback=bgColor,
|
|
}
|
|
|
|
\graphicspath{{build/figures/}{images/}}
|
|
|
|
\newfontfamily{\Tahoma}{Tahoma}
|
|
\newfontfamily{\SourceSans}{Source Sans Pro}
|
|
% Looks like the font from google fonts has a different case,
|
|
% so I'm stuck with scanning for existing fonts
|
|
\IfFontExistsTF{Source Sans Pro SemiBold}{
|
|
\newfontfamily{\SourceSansSB}[UprightFont={* SemiBold}]{Source Sans Pro}
|
|
}{
|
|
\IfFontExistsTF{Source Sans Pro Semi-Bold}{
|
|
\newfontfamily{\SourceSansSB}[UprightFont={* Semi-Bold}]{Source Sans Pro}
|
|
}{
|
|
\newfontfamily{\SourceSansSB}[UprightFont={* Semibold}]{Source Sans Pro}
|
|
}
|
|
}
|
|
\setmainfont{Source Sans Pro}
|
|
% \newfontfamily{\ossb}[UprightFont={* Semibold}]{Open Sans}
|
|
\newcommand{\entity}[1]{{\SourceSansSB #1}}
|
|
\newcommand{\entityb}[1]{#1}
|
|
\newcommand{\person}[2]{#1 #2}
|
|
\newcommand{\term}[1]{\textit{#1}}
|
|
\newcommand{\figref}[1]{[Figure~\ref{#1}]}
|
|
\newcommand{\inputfig}[1]{\input{figures/#1}}
|
|
|
|
\title{Rapport de stage ST40 - A2022}
|
|
\author{Adrien Burgun}
|
|
\date{Automne 2022}
|
|
|
|
|
|
\setutbmfrontillustration{assets/priscilla-du-preez-BjhUu6BpUZA-unsplash}
|
|
\setutbmtitle{Stage développeur full stack}
|
|
\setutbmsubtitle{Rapport de stage ST40 - A2022}
|
|
\setutbmstudent{BURGUN Adrien}
|
|
\setutbmstudentdepartment{Département Informatique}
|
|
\setutbmstudentpathway{}
|
|
\setutbmcompany{Moment - T\&M}
|
|
\setutbmcompanyaddress{3 boulevard Richard Lenoir\\75011 Paris}
|
|
\setutbmcompanywebsite{\href{https://moment.tech/}{moment.tech}}
|
|
\setutbmcompanytutor{PERRIN Pierre}
|
|
\setutbmschooltutor{HOLWECK Frederic}
|
|
% From https://moodle.utbm.fr/pluginfile.php/117803/mod_book/chapter/595/CARNET-A22-20220711.pdf
|
|
\setutbmkeywords{Télécommunications - Informatique - Développement logiciel - Logiciel de gestion - Logiciel de réseaux}
|
|
\setutbmabstract{
|
|
J'ai effectué mon stage ST40 au sein du département de développement full-stack de l'entreprise \entity{Moment},
|
|
une start-up française basée à Paris et fondée en 2013.
|
|
\newline
|
|
|
|
Moment propose à ses clients des offres de divertissement pour l'aviation, le secteur maritime, le secteur ferroviaire et
|
|
plus récemment le domaine de la santé. Leur offre principale, la \og Flymingo Box \fg, est une solution tout-en-un qui permet
|
|
aux passagers de directement accéder à une librairie de divertissement depuis leurs appareils mobiles.
|
|
\newline
|
|
|
|
J'ai pu contribuer au développement de fonctionnalités pour la nouvelle offre déployée dans les cliniques de santé,
|
|
à l'optimisation de l'application tournant sur des télévisions connectées et au maintien du code front-end.
|
|
}
|
|
|
|
\begin{document}
|
|
% \maketitle
|
|
\makeutbmfrontcover{}
|
|
|
|
\section*{Introduction}
|
|
|
|
J'ai effectué mon stage ST40 au sein du département de développement full-stack de l'entreprise \entity{Moment},
|
|
une start-up française basée à Paris et fondée en 2013.
|
|
|
|
J'étais pendant ce stage sous la supervision de \person{Pierre}{Perrin},
|
|
et j'ai intégré le cycle hebdomadaire de développement de l'équipe.
|
|
|
|
J'ai pu travailler en grande partie sur une application back-end développée pour le nouveau secteur de commerce de l'entreprise,
|
|
et sur une application front-end tournant sur des télévisions connectées.
|
|
|
|
\section*{Remerciements}
|
|
|
|
Je tiens à remercier toutes les personnes qui m'ont apporté de l'aide durant mon stage et lors de la rédaction de ce rapport.
|
|
|
|
Je souhaite en particulier remercier mon maître de stage et chef d'équipe, \person{Pierre}{Perrin},
|
|
dont la bienveillance et le fiabilité m'ont inspirés.
|
|
\\
|
|
J'aimerais aussi remercier chaqu'un de mes collègues de l'équipe de développement full-stack,
|
|
avec qui j'ai été heureux de travailler, qui ont su répondre à mes questions,
|
|
et qui m'ont aidé à faire un travail de meilleure qualité.
|
|
|
|
Je souhaite remercier l'\entity{UTBM} et les différents membres de son équipe pédagogique,
|
|
pour m'avoir apporté les connaissances et outils que j'ai pu mettre en oeuvre durant ce stage.
|
|
Je souhaite en particulier remercier \person{Serges}{Iovleff} pour m'avoir appris les outils de statistique qui se sont avérés avoir été indispensables pour analyser mes mesures,
|
|
et \person{Philippe}{Descamps} pour m'avoir ouvert les yeux aux problématiques du domaine de la synchronisation.
|
|
|
|
Je souhaite remercier l'entièreté de l'entreprise \entity{Moment},
|
|
pour m'avoir accueilli chaleureusement et pour m'avoir donné il y a presque un an l'opportunité de réaliser mon stage parmis eux.
|
|
|
|
Je souhaite enfin remercier ma famille et mon cercle d'amis pour leur soutien inconditionnel tout au long de ce stage,
|
|
et pour avoir bien voulu relire ce document.
|
|
|
|
\newpage
|
|
{
|
|
\Tahoma
|
|
\tableofcontents
|
|
}
|
|
\newpage
|
|
|
|
\section{Présentation}
|
|
|
|
\subsection{Présentation de l'entreprise}
|
|
|
|
\entity{Moment} est une entreprise fondée en 2013, avec pour but de rendre l'expérience des voyageurs plus simple,
|
|
plus plaisante et plus connectée \cite{momenttech}.
|
|
Pour y parvenir, \entity{Moment} propose une plateforme digitale déployée dans les avions, les trains, les bateaux
|
|
de croisière et plus récemment les cliniques de santé.
|
|
Cette plateforme permet aux voyageurs de se connecter à un réseau wifi avec leurs propres appareils, et de profiter
|
|
de divertissements directement sur ceux-ci.
|
|
|
|
À ce jour, \entityb{Moment} s'apprête à compter 10 ans de travail dans ce domaine, et possède une trentaine d'employés.
|
|
\entityb{Moment} a lancé plus de 60 projets dans plus de 15 pays, et ses produits sont utilisés par plus de 10 millions
|
|
de passagers par jour.
|
|
|
|
\entityb{Moment} fournit ses services aujourd'hui à \entity{TGV InOui}, \entity{Air Belgium}, \entity{Brittany Ferries} et \entity{Air Senegal}.
|
|
\entityb{Moment} a aussi dans le passé fourni ses services à \entity{Air France} et \entity{l'Aéroport de Paris}.
|
|
|
|
Après avoir déployé déjà 500 serveurs, l'entreprise \entityb{Moment} a lancé en 2021 une filiale nommée \entity{Moment Care},
|
|
qui se spécialise dans le divertissement dans le domaine de la santé:
|
|
\entity{Moment Care} souhaite révolutionner le contenu des offres de divertissement dans les établissements
|
|
de santé, et la manière d'y accéder \cite{momentcare}.
|
|
|
|
\subsection{Présentation du lieu de stage}
|
|
|
|
Lors de mon stage, les locaux de \entityb{Moment} se trouvaient à Paris, près de la place de la Bastille.
|
|
Ces bureaux sont partagés par la grande majorité des employés de \entityb{Moment}, qui sont organisés en
|
|
différents pôles:
|
|
|
|
\begin{description}
|
|
\item[Développement full-stack:] C'est au sein de cette équipe que mon stage s'est déroulé,
|
|
sous la supervision de son chef d'équipe, Pierre Perrin.
|
|
|
|
\item[Marketing \& Design:] Ce pôle s'occupe de l'image de l'entreprise et des produits, à la fois sur
|
|
les réseaux sociaux et auprès des clients présents et futurs.
|
|
Cette équipe décide notamment du design des différentes applications, qui est envoyé à l'équipe
|
|
\entity{Fullstack R\&D} pour implémentation.
|
|
|
|
\item[PMO (Project Management Office):] Ce pôle est constitué de chefs de projets, qui gèrent les demandes
|
|
des clients, les dates limites, les statistiques à relayer aux clients et les difficultés techniques.
|
|
|
|
\item[Contenu:] L'équipe contenu s'occupe de maintenir la base de contenus disponibles sur les
|
|
différentes plateformes déployées. Elle négocie auprès des distributeurs de films, de séries
|
|
et de magazines, et elle maintient à jour le \term{Content Management System} (CMS).
|
|
|
|
\item[Business Developers:] Cette équipe est en contact avec nos clients actuels et de potentiels clients
|
|
futurs. Elle met en place le business plan, amène à l'entreprise de nouvelles opportunités
|
|
de partenariat et négocie les prix lors de la vente des produits de l'entreprise.
|
|
|
|
\item[Moment Care:] Une équipe est dédiée à la filiale \entity{Moment Care}.
|
|
Cette équipe s'occupe de vendre les produits de \entity{Moment Care},
|
|
de suivre leur développement et de déployer les produits de \entityb{Moment Care} chez les clients.
|
|
|
|
\item[Administratif:] Ce pôle est constitué du directeur des resources humaines,
|
|
qui s'assure que l'entreprise a les employés nécessaires pour travailler.
|
|
Ce pôle s'occupe aussi de la finance et du budget de l'entreprise.
|
|
|
|
\item[Tech:] Le pôle technologie se divise en deux grandes équipes, les \entity{DevOps} et les \entity{Dev Full-Stack},
|
|
ainsi qu'une nouvelle équipe qui mène le développement du hardware pour la seconde génération de serveurs que l'entreprise souhaite bientôt proposer.
|
|
|
|
\item[DevOps:] L'équipe \entity{DevOps} développe des outils,
|
|
méthodes et processus pour automatiser les différents besoins des applications à déployer.
|
|
L'équipe gère entre autres le déploiement des applications sur les serveurs envoyés dans les avions,
|
|
la configuration du système d'exploitation de ces serveurs.
|
|
|
|
\item[Full Stack:] L'équipe \entity{Full Stack} développe les différentes applications front-end et back-end que \entity{Moment} propose.
|
|
Ces applications s'étendent du serveur de streaming de médias aux applications web et aux applications mobiles.
|
|
C'est également l'équipe que j'ai rejoint durant ce stage.
|
|
\end{description}
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{poles}
|
|
\caption{Diagramme des pôles de l'entreprise}
|
|
\end{figure}
|
|
|
|
\newpage
|
|
\subsection{Produits de Moment}
|
|
|
|
\subsubsection{Mood}
|
|
|
|
% TODO: crop images
|
|
\begin{wrapfigure}{r}{0.25\textwidth}
|
|
\centering
|
|
\includegraphics[width=0.25\textwidth]{flymingo-box}
|
|
\caption*{Flymingo Box}
|
|
\includegraphics[width=0.25\textwidth]{mood-digital}
|
|
\caption*{Mood Digital}
|
|
\end{wrapfigure}
|
|
|
|
\textbf{Mood} (auparavant appelé \entity{Flymingo}) est la suite de produits principale de \entityb{Moment},
|
|
qui est aujourd'hui déployée dans plus de 200 avions, 30 bateaux et 300 trains.
|
|
La plateforme \entity{Flymingo} permet aux compagnies de transport de proposer du divertissement à ses passagers,
|
|
avec une installation et une gestion simplifiée et sans le besoin d'installer des tablettes dans les sièges des passagers.
|
|
|
|
Ce produit est composé d'un serveur, nommé \entity{Flymingo Box}, qui émet son propre réseau wifi,
|
|
sur lequel les passagers peuvent se connecter pour profiter des divertissements.
|
|
|
|
Différentes applications sont développées autour de la \entity{Flymingo Box}:
|
|
|
|
\begin{description}
|
|
\item[Mood Entertainment] aussi appelé \entity{Mood Digital}, est une application web,
|
|
sur laquelle les passagers se connectent pour regarder des films,
|
|
lire de la presse ou jouer à des jeux dans le navigateur.
|
|
L'application peut être utilisée sur les téléphones, tablettes et ordinateurs.
|
|
\\
|
|
L'application peut aussi être adaptée aux besoins des clients, pour être en accord avec le langage visuel de leur marque
|
|
ou pour supporter différents services comme l'achat en ligne, les informations de voyage, etc.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Mood TV] est une application tournant sur les télévisions connectées et permettant de profiter du contenu directement sur une télévision.
|
|
Ces télévisions sont surtout utilisées sur les bateaux de croisière.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Content API et Media API] sont deux \term{API} (interfaces de programmation) qui gèrent les films et autres médias stockées sur la box,
|
|
ainsi que leurs informations (titres, durée, etc.).
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[La \og Crew App \fg] est une application sur \entity{iOS} pour les membres de l'équippage,
|
|
qui permet de mettre en pause la lecture des médias pour faire des annonces aux passagers,
|
|
accéder aux informations des achats faits sur l'application \entity{Mood Digital}.
|
|
\end{description}
|
|
|
|
\subsubsection{Mint}
|
|
|
|
\begin{wrapfigure}{r}{0.25\textwidth}
|
|
\centering
|
|
\includegraphics[width=0.25\textwidth]{mint-digital}
|
|
\caption*{Mint Digital}
|
|
\end{wrapfigure}
|
|
|
|
\textbf{Mint} est la plateforme que \entityb{Moment} développe et déploie en ce moment dans les cliniques de santé.
|
|
Elle vise à permettre aux patients des cliniques de profiter de divertissements de qualité tout le long de leur séjour.
|
|
|
|
Les applications développées autour de cette plateforme sont:
|
|
|
|
\begin{description}
|
|
\item[Mint Admin,] qui est une application web permettant aux clinique de fixer les prix
|
|
pour les offres que celles-ci souhaite mettre à disposition, ainsi que de gérer l'attribution des différents
|
|
appareils (comme les télévisions et les tablettes) aux chambres, des différents patients à ces chambres,
|
|
et des offres aux patients.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Mint Digital,] qui est une application web que les patients des cliniques utilisent
|
|
pour se connecter, acheter des offres en ligne, regarder des films, documentaires, magazines et écouter de la musique.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Mint Service,] qui est le back-end avec lequel \entity{Mint Admin} et \entity{Mint Digital} communiquent.
|
|
\entity{Mint Service} expose une API permettant à \entity{Mint Admin} de faire des modifications aux offres et aux patients,
|
|
et à \entity{Mint Digital} de permettre aux utilisateurs de se connecter et d'acheter des offres en ligne.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Mint Tab,] qui est une application pour tablettes \term{iPad}, qui permet aux patients des cliniques
|
|
d'accéder au même contenu que sur \entity{Mint Digital}, sans avoir besoin d'utiliser leurs propres appareils.
|
|
\end{description}
|
|
|
|
\begin{description}
|
|
\item[Mint TV,] qui sera une application tournant sur les télévisions connectées, qui permet aux patients des
|
|
cliniques d'accéder au même contenu que sur \entity{Mint Digital}, en plus de pouvoir regarder les flux de la télévision.
|
|
\end{description}
|
|
|
|
\subsection{Présentation de l'équipe Développement R\&D}
|
|
|
|
\subsubsection{Méthode de travail}
|
|
|
|
L'équipe \entity{Développement Full-Stack} fonctionne avec la méthode Scrum, qui est une méthode Agile. \cite{cohen2004introduction}
|
|
|
|
Notre chef d'équipe reçoit les spécifications des projets, les retours de recettes et les bugs des équipes \entity{PMO} et \entity{Design}.
|
|
En chaque début de semaine, sur la base de ces demandes et retours, nous définissions les thèmes,
|
|
tâches et priorités pour la semaine durant la réunion \og weekly \fg.
|
|
|
|
Chaque matin, nous faisons aussi une réunion \og daily \fg,
|
|
où chaqu'un présente le travail qu'iel a fait la veille, ainsi que ce qu'iel prévoit de faire ce jour.
|
|
|
|
Les tâches de la semaine sont inscrites sur \entity{Jira} dans un \term{Sprint} pour la semaine,
|
|
où elles seront facilement visibles et leur statut sera facilement suivable.
|
|
|
|
L'attribution des tâches se fait en majorité sur la base du volontariat, avec les tâches à haute priorité à faire en premier:
|
|
lorsqu'il y a une tâche disponible que l'on souhaite faire,
|
|
on s'assigne à celle-ci sur \entity{Jira}, et on passe le statut de la tâche de \textit{À faire} à \textit{En cours}. \figref{jira}
|
|
On crée alors une \term{Merge Request} sur \entity{GitLab} (l'équivalent des \term{Pull Request} sur \entity{GitHub})
|
|
pour accueillir les changements à faire pour cette tâche.
|
|
|
|
Une fois que la tâche est implémentée,
|
|
on marque la \term{Merge Request} en mode \og Ready \fg et on passe la tâche sur \entity{Jira} au statut \textit{Prêt à review}.
|
|
Un autre membre de l'équipe doit alors \term{review} les modifications au code,
|
|
pour s'assurer que les attentes en termes de qualité de code sont remplies.
|
|
|
|
Si la \term{review} relève des problèmes, alors ceux-ci doivent être adressés.
|
|
Dans le cas contraire, les changements de la \term{Merge Request} sont incorporés dans la branche \texttt{develop} et la tâche sur \entity{Jira} marquée comme \textit{Faite}.
|
|
|
|
Les tâches qui ne sont pas faites pendant un \term{sprint} sont gardées dans un \term{Backlog},
|
|
afin de pouvoir les intégrer au sprint de la semaine d'après,
|
|
ou pour les faire un jour où les taches d'un Sprint sont finies avant la fin de la semaine,
|
|
en fonction de leur priorité.
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{jira}
|
|
\caption{Exemple de tâche inscrite sur Jira; cette tâche est \textit{En Cours}}
|
|
\label{jira}
|
|
\end{figure}
|
|
|
|
\subsubsection{Outils utilisés}
|
|
|
|
Nous utilisons une diversité d'outils pour développer du code,
|
|
allant de l'éditeur de texte jusqu'aux outils automatisant le test du code affecté par un changement.
|
|
|
|
\newcommand{\logowidth}{0.15\textwidth}
|
|
|
|
\textbf{Git} \cite{git} est utilisé pour gérer le versionnage du code: cet outil permet de stocker les modifications
|
|
apportées au code sous forme de \og commits \fg, et de facilement rassembler et gérer les modifications faites par plusieurs personnes.
|
|
|
|
% wrapfigure does not work with my \entity :(
|
|
\begin{wrapfigure}{r}{\logowidth}
|
|
\centering
|
|
\includesvg[width=\logowidth]{gitlab_logo.svg}
|
|
\caption*{GitLab \cite{gitlabsvgs}}
|
|
\end{wrapfigure}
|
|
|
|
\textbf{GitLab} \cite{gitlab} est utilisé pour stocker les repositories de l'entreprise en ligne,
|
|
pour stocker en ligne les paquets \entity{npm} \cite{npm} et pour lancer automatiquement les tâches de \term{Continuous Integration}.
|
|
|
|
\textbf{VSCode} \cite{vscode} est un éditeur de texte utilisé par presque toute l'équipe pour modifier ou naviguer le code.
|
|
|
|
\textbf{Slack} \cite{slack} est une plateforme de messagerie instantanée,
|
|
que l'entreprise préfère aux e-mails pour la communication indirecte.
|
|
|
|
\textbf{Jira} \cite{jira} est un logiciel de gestion et de suivi de tickets,
|
|
que notre équipe utilise pour suivre les tâches à faire durant la semaine,
|
|
quelles tâches sont en cours de développement, prêtes à être revues ou finies,
|
|
ainsi que pour garder un \term{backlog} des tâches à faible priorité.
|
|
|
|
\textbf{Confluence} \cite{confluence} est un logiciel qui permet à l'équipe de regrouper des documentations qui n'ont pas de place claire au sein du code.
|
|
|
|
\textbf{NX} \cite{nx} est un outil simplifiant la gestion de \og monorepositories \fg (des repositories contenant plusieurs applications et librairies).
|
|
\textbf{NX} est utilisé pour la repository de l'équipe qui contient Mint Service, Mint Admin et Mood TV.
|
|
L'objectif au long terme et de migrer l'ensemble des projets de l'équipe sur cette repository,
|
|
car elle permet de facilement réutiliser du code entre les projets et de synchroniser des modifications entre projets.
|
|
|
|
\begin{wrapfigure}{r}{\logowidth}
|
|
\centering
|
|
\includesvg[width=\logowidth]{ts-logo-128.svg}
|
|
\caption*{TypeScript \cite{typescriptlogo}}
|
|
\end{wrapfigure}
|
|
|
|
\textbf{Typescript} \cite{typescript} est une extension au langage JavaScript, qui ajoute un système de vérification de types.
|
|
Typescript permet d'éviter une classe de bugs causés par des valeurs passées à des fonctions attendant un certain type en entrée.
|
|
\\
|
|
Typescript existe aujourd'hui en tant que langage qui doit être transpilé en javascript avant de pouvoir être utilisé dans Node.JS ou dans un navigateur,
|
|
mais certains interpréteurs (comme \textbf{Bun} \cite{bun}) supportent le typescript,
|
|
et il existe une initiative pour standardiser un système permettant d'ajouter des types en javascript sans devoir le traduire.
|
|
|
|
\textbf{React} \cite{react} est une librairie JavaScript qui permet de facilement construire des interfaces graphiques fortement intéractives.
|
|
React permet d'encapsuler l'affichage et la gestion d'évènements dans des \og composants \fg, qui peuvent être réutilisés de manière modulaire.
|
|
React propose aussi une extension de JavaScript, qui permet d'écrire du code HTML directement dans le code JavaScript,
|
|
ainsi que d'insérer des composants avec la même syntaxe.
|
|
|
|
\textbf{Next.JS} \cite{nextjs} est un framework simplifiant l'utilisation de React pour des plus grandes applications:
|
|
NextJS permet de séparer l'application en différentes pages,
|
|
et de naviguer de manière ininterrompue entre ces pages.
|
|
NextJS a également pour but d'optimiser la vitesse de chargement de ces pages, en faisant un maximum du rendu de ces pages côté serveur.
|
|
|
|
\textbf{Nest.JS} \cite{nestjs} est un framework simplifiant la création d'applications back-end.
|
|
NestJS utilise l'injection de dépendance pour permettre de plus facilement faire grandir une application back-end,
|
|
et propose de nombreux outils pour gérer les requêtes, les bases de données et la documentation.
|
|
|
|
\subsubsection{Contrôle et assurance de qualité}
|
|
|
|
L'équipe a plusieurs outils en place pour assurer la qualité du code et des applications tout au long du développement:
|
|
|
|
\begin{description}
|
|
\item[Tests unitaires et tests d'intégration] Des tests unitaires sont écrits en parallèle de code \og métier \fg,
|
|
afin de tester de manière automatique et isolée le bon fonctionnement d'un composant en particulier.
|
|
|
|
Des tests d'intégration sont aussi écrits, qui testent de manière automatique une librairie ou une application entière,
|
|
afin de vérifier le bon fonctionnement de cette librairie ou de l'application.
|
|
|
|
Ces tests sont exécutés sur nos machines,
|
|
afin de pouvoir rapidement corriger tout morceau de code qui aurait été accidentellement cassé lors d'une modification.
|
|
|
|
% TODO: citation for CI
|
|
\item[Continuous Integration] En plus de tester le code localement,
|
|
le code est aussi compilé et testé sur un serveur via les outils de \entity{GitLab} pour l'intégration continue.
|
|
|
|
Executer ces tests sur un serveur permet de valider le comportement de l'application à tout moment du projet par rapport à une base commune,
|
|
et de s'assurer qu'une combinaison de modifications ne mène pas à un problème.
|
|
|
|
\item[Linting et formatting] Nous utilisons des outils comme \entity{ESLint} et \entity{Prettier} pour vérifier la consistance
|
|
de la forme du code dans les différents projets.
|
|
\entity{ESLint} nous permet de mettre en place des règles vérifiées automatiquement,
|
|
et qui lorsqu'elles sont suivies diminuent le risque d'avoir du comportement inattendu dans l'application.
|
|
|
|
\item[Merge Reviews] Avant qu'une modification ne soit incorporée dans la branche commune,
|
|
elle doit être vérifiée et validée par un humain.
|
|
|
|
Cette étape complète celle d'\entity{ESLint}, car certaines règles de style ne peuvent pas être appliquées automatiquement.
|
|
La vérification des modifications par un humain permet aussi d'éviter des potentiels bugs ou points de confusion.
|
|
|
|
\item[Recettes] Pour valider les produits, un serveur de test, le \og lab \fg,
|
|
est à disposition pour déployer et tester localement les différents projets.
|
|
Cette version déployée localement est ensuite testée par les chefs de projet et d'autres personnes de l'entreprise pour déceler des problèmes qui n'auraient pas été trouvés pendant le développement.
|
|
|
|
\item[Semantic versioning] Nous utilisons le versionnage sémantique pour nos projets et pour les dépendance des projets.
|
|
Le versionnage sémantique permet d'encoder dans le numéro de version du programme quel type de mise à jour il y a entre deux versions.
|
|
Le format du versionnage sémantique est \;\texttt{major.minor.patch}, où:
|
|
|
|
\begin{itemize}
|
|
\item Si seulement \texttt{patch} change, alors la mise à jour ne modifie pas la fonctionnalité, mais corrige uniquement des bugs.
|
|
\item Si \texttt{minor} ou \texttt{patch} changent, alors la mise à jour ajoute uniquement de la fonctionnalité; faire la mise à jour ne devrait pas casser du code utilisant la librairie en question.
|
|
\item Si \texttt{major} change, alors la mise à jour introduit des changements cassants: il faudra alors faire des changements au code utilisant la librairie.
|
|
\end{itemize}
|
|
\end{description}
|
|
|
|
\newpage
|
|
\section{Organisation du stage}
|
|
|
|
% Durant ce stage, j'ai rejoint l'équipe \entity{Développement Full-Stack - Recherche \& Développement},
|
|
% afin de contribuer au développement des applications front-end et back-end de l'entreprise,
|
|
% à l'assurance de qualité de ces applications et aux choix techniques faits pour ces applications.
|
|
|
|
\subsection{Thèmes définis avant le stage}
|
|
|
|
% Note: commencé avec 9 personnes, puis est passé à 7 personnes
|
|
Durant mes échanges avec mon tuteur de stage et le directeur des ressources humaines de \entity{Moment},
|
|
nous nous étions convenus que j'allais intégrer l'équipe \entity{Développement Full-Stack - Recherche \& Développement} en tant que développeur.
|
|
|
|
Mes tâches au sein de cette équipe seraient de:
|
|
|
|
% https://utbm.jobteaser.com/en/job-offers/8878261-moment-developpeur-se-web-fullstack-stage-ou-alternance
|
|
% TODO: clarifier qu'est ce qui constitue un projet R&D
|
|
\begin{itemize}
|
|
\item Développer de nouvelles fonctionnalités (notamment de nouvelles \term{API} (interfaces de programmation) et de leur intégration côté client)
|
|
\item Documenter le code (sous forme de commentaires et sous forme de documentation technique)
|
|
\item Participer aux choix fonctionnels (quelles technologies utiliser, comment organiser des librairies, etc.),
|
|
et participation à leur implémentation
|
|
\item Participer à la définition de la roadmap technique, et au suivi de cette roadmap
|
|
\item Aider à améliorer les solutions déjà existantes (amélioration de performances, de maintenabilité, etc.)
|
|
\item Participation aux projets Recherche \& Développement
|
|
\end{itemize}
|
|
|
|
Au moment de mes entretiens pour ma candidature de stage,
|
|
les thèmes principaux sur lesquels l'équipe travaillaient étaient le développement de \entity{Mood Digital}
|
|
et de \entity{Mood TV}, ainsi qu'un peu de développement pour \entity{Mint}.
|
|
|
|
J'ai aussi pu apprendre que l'équipe utilise la librairie \entity{React} et le framework \entity{Next.JS} pour ses applications front-end,
|
|
et le moteur javascript \entity{Node.JS} avec le framework \entity{Nest.JS} pour ses applications back-end.
|
|
|
|
J'espérais avant le stage pouvoir surtout travailler sur la partie back-end,
|
|
car j'avais jusqu'à ce moment-là peu d'expérience avec \entity{React},
|
|
et le peu d'expérience que j'avais ne m'inspirait pas confiance en cette technologie.
|
|
|
|
J'ai néanmoins décidé d'apprendre en amont du stage la librairie \entity{Solid.JS} \cite{solidjs},
|
|
car je n'avais jusqu'à maintenant que fait du développement front-end \og vanilla \fg (sans librairie ou framework).
|
|
\entity{Solid.JS} se veut être une librairie de développement front-end plus simple, plus réactive et plus efficace que \entity{React},
|
|
et l'apprendre m'a permis d'avoir une première expérience avec la programmation réactive.
|
|
|
|
\subsection{Premiers jours de stage}
|
|
|
|
À mon arrivée au stage, j'ai récupéré l'ordinateur portable qui m'a été préparé avec une installation d'Ubuntu,
|
|
puis j'ai installé un environnement de développement d'applications dessus (différents outils en ligne de commande,
|
|
un éditeur de texte et un gestionnaire de fenêtre avec lequel je suis plus à l'aise).
|
|
|
|
J'ai aussi reçu la documentation d'\og onboarding \fg,
|
|
qui contient une liste de documentations avec lesquelles je devais me familiariser afin de pouvoir travailler efficacement.
|
|
J'ai parcouru ces documentations et ai testé certaines des fonctionnalités décrites dans celles-ci dans un mini-projet de test.
|
|
|
|
L'équipe se réunit chaque matin pour discuter des nouveautés et du travail de la veille,
|
|
et pour annoncer sur quoi chaqu'un prévoit de travailler ce jour-ci.
|
|
J'ai participé à ces réunions dès mon premier jour,
|
|
ce qui m'a permis de rapidement construire une compréhension des problématiques récurrente et de la dynamique des différentes applications.
|
|
|
|
Nous sommes encouragés dans l'équipe à travailler en programmer en paire,
|
|
et en général à collaborer ensemble.
|
|
Ma première contribution au code a été faite en programmation en paire,
|
|
après avoir remarqué que la logique pour la complexité des mots de passe dans \entity{Mint Admin} était fausse.
|
|
Faire cette contribution en pair programming m'a permis d'être guidé dans le processus d'envoi et de vérification de contributions,
|
|
ainsi que d'être guidé dans l'organisation de la repository.
|
|
|
|
\subsection{Planning}
|
|
|
|
Durant ce stage, j'ai surtout travaillé sur le développement de nouvelles fonctionnalités et la correction de bugs sur les applications suivantes:
|
|
|
|
\begin{itemize}
|
|
\item \entity{Mint Service}: développement de nouvelles fonctionnalités, sécurisation de l'API
|
|
\item \entity{Mood TV}: optimisation des performances, correction de bugs, refactors
|
|
\item \entity{Mint Admin}: correction de bugs
|
|
\item \entity{Mint Digital}: correction de bugs, développement de la fonctionnalité pour gérer l'accès à internet
|
|
\end{itemize}
|
|
|
|
Comme notre équipe travaille en mode Agile, les sujets et priorités étaient définis semaine par semaine.
|
|
Tout de moins, mon stage peut être découpé en trois périodes:
|
|
|
|
\begin{itemize}
|
|
\item Au début du stage, la priorité était de préparer \entity{Mint Service} et \entity{Mint Admin}
|
|
pour une première version à envoyer au premier client de \entity{Moment Care} (semaines 1-7).
|
|
\item Puis, une fois cette version déployée,
|
|
nous avons travaillé sur l'implémentation des fonctionnalités manquantes dans cette première version (semaines 8-16).
|
|
\item Enfin, nous avons repris le projet \entity{Mood TV},
|
|
qui avait alors vu très peu de modifications (à part mes optimisations) depuis plusieurs mois,
|
|
afin de le refactor pour préparer l'implémentation de \entity{Mint TV}, qui réutilisera la majorité du code de \entity{Mood TV} (semaines 18-23).
|
|
\end{itemize}
|
|
|
|
Comme mon stage commençait le 1$^{\text{er}}$ Septembre 2022 (un jeudi),
|
|
j'ai décidé de numéroter le sommaire suivant de telle manière à ce que la semaine du 5 Septembre soit la semaine 1.
|
|
|
|
\inputfig{timeline}
|
|
|
|
\newpage
|
|
\section{Travail réalisé sur Mint Service}
|
|
|
|
Mint Service est une application construite avec \entity{NestJS}.
|
|
NestJS est un framework permettant de construire des applications côté serveur en JavaScript,
|
|
et de s'assurer que ces applications soient à la fois sûres, efficaces et flexibles \cite{nestjs}.
|
|
|
|
\subsection{NestJS et organisation du code}
|
|
|
|
% TODO: hiérarchiser l'argumentaire
|
|
|
|
NestJS emploie à son coeur les principes de \term{modularité} et d'\term{inversion de contrôle} (\term{IoC}):
|
|
\\
|
|
L'application est séparée en différents modules, qui eux-mêmes contiennent des \term{services} et des \term{contrôleurs}. \figref{nestjs3}
|
|
|
|
\begin{description}
|
|
\item[Les services] (aussi appelés \term{providers}) contiennent l'implémentation de la logique nécessaire
|
|
pour répondre aux besoins de l'application: contacter une base de donnée, traiter l'information, faire des
|
|
requêtes réseau, etc.
|
|
|
|
\item[Les contrôleurs] sont responsables d'accepter les requêtes des \entity{clients}, de les transformer, de vérifier
|
|
leur légitimité et d'appeler les méthodes des \entity{services} avant de transformer la réponse à envoyer au \entity{client}.
|
|
\\
|
|
NestJS fournit un ensemble d'outils simplifiant l'implémentation des contrôleurs.
|
|
|
|
\item[Les modules] s'occupent de gérer la configuration des \entity{services} et des \entity{contrôleurs}:
|
|
chaque \entity{module} indique à NestJS quels \entity{services} et \entity{contrôleurs} il contient,
|
|
ainsi que comment NestJS doit construire ces services dans les cas plus complexes.
|
|
\\
|
|
Les modules peuvent exporter un sous-ensemble de leurs services, ce qui permet de rapidement
|
|
réutiliser des services en important le module leur étant associé. \figref{nestjs2}
|
|
\end{description}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics{nestjs3}
|
|
\caption{Organisation d'un module NestJS typique}
|
|
\label{nestjs3}
|
|
\end{figure}
|
|
|
|
|
|
La séparation entre contrôleurs et services permet d'avoir une claire \term{séparation des préoccupations}:
|
|
|
|
\begin{itemize}
|
|
\item Les contrôleurs s'occupent de gérer, valider et répondre aux requêtes faites par un client;
|
|
\item Les services s'occupent de l'implémentation de la logique permettant de satisfaire la requête;
|
|
\item Les modules s'occupent de configurer les services et les contrôleurs, et permettent la modularisation du code.
|
|
\end{itemize}
|
|
|
|
Pour limiter le couplage entre les services, et pour simplifier la réutilisation des services,
|
|
NestJS permet de faire de l'\term{injection de dépendances} par \term{constructeur sans défaut} \cite{yang2008empirical} dans les services et dans les contrôleurs,
|
|
d'une manière similaire au framework \entity{Spring} en \entity{Java}. \figref{nestjs1}
|
|
|
|
% TODO: trouver une source
|
|
% Separation of concerns
|
|
% https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=0fd24fa8ac2a77c4709103eb8a179fec38311fe8
|
|
% "On the importance of the separation-of-concerns principle in secure software engineering"
|
|
Il devient ainsi plus simple avec ce système de modifier différentes parties du code sans accidentellement casser une autre partie du code.
|
|
Il est aussi plus simple de tester du code organisé de la manière suivante, car les services et les contrôleurs peuvent
|
|
être placés dans un environnement isolé et testés individuellement ou collectivement.
|
|
|
|
|
|
\subsection{Système d'envoi de mail}
|
|
|
|
\subsubsection{Analyse et prototype}
|
|
|
|
On m'a donné en début de stage la tâche d'implémenter un système d'envoi de mail pour \entity{Mint Service}.
|
|
Ce système prend la forme de plusieurs librairies, qui doit remplir les conditions suivantes:
|
|
|
|
\begin{itemize}
|
|
\item Il doit être facilement réutilisable dans d'autres projets.
|
|
\item Il doit permettre de changer de fournisseur de service pour l'envoi d'e-mails sans devoir faire de grands changements du côté des utilisateurs de la librairie.
|
|
\item Il doit pouvoir envoyer des e-mails qui s'affichent de la manière voulue sur la majorités des boîtes mail.
|
|
\item Il doit pouvoir envoyer des e-mails contenant des informations sur l'utilisateur; cette étape devra être faite avec le service de \term{templates} d'e-mail sur \entity{Amazon SES} \cite{amazonses} \cite{amazonsestemplate}
|
|
\item Il doit permettre de décrire le contenu des e-mails d'une manière facilement maintenable et réutilisable.
|
|
\end{itemize}
|
|
|
|
Mon travail a commencé par l'analyse de différentes solutions permettant d'adresser les deux dernières conditions.
|
|
|
|
Par le passé, l'entreprise avait déjà utilisé le moteur \entity{Handlebars} \cite{handlebars},
|
|
une librairie de recherche et de remplacement de texte, qui permet d'avoir des formes simples de logique,
|
|
comme des conditions ou des boucles.
|
|
|
|
Bien que \entity{Handlebars} fonctionne bien pour insérer les informations de l'utilisateur dans les mails,
|
|
il ne permet pas de facilement réutiliser du code, qui est souvent complexe afin d'assurer un bon affiche sur les
|
|
différents programmes de boîte mail.
|
|
|
|
J'ai donc cherché à avoir un système de \og composants \fg dans la solution choisie, afin de pouvoir construire des couches
|
|
d'abstraction et de réutiliser facilement le code. Ces composants doivent, plus spécifiquement:
|
|
|
|
\begin{itemize}
|
|
\item Pouvoir être appelés avec différentes valeurs en arguments
|
|
\item Pouvoir appeler d'autres composants
|
|
\end{itemize}
|
|
|
|
J'ai sélectionné deux moteurs de recherche et de remplacement de texte (\term{Search and Replace}): \entity{Handlebars} \cite{handlebars} et \entity{Mustache} \cite{mustache}.
|
|
Ces deux moteurs opèrent de manière similaire: ils lisent le code source,
|
|
et lorsqu'une variable indiquée par les balises \texttt{\{\{} et \texttt{\}\}} est rencontrée,
|
|
celle-ci est subtituée par la valeur assignée au nom de cette variable.
|
|
|
|
\entity{Handlebars} et \entity{Mustache} peuvent tous les deux être utilisés pour implémenter le système de composants voulu,
|
|
car ils permettent de définir des variables spéciales, que les deux moteurs appelent \term{partials},
|
|
qui peuvent appeler du code javascript arbitraire.
|
|
|
|
J'ai enfin fait un prototype en \entity{Typescript},
|
|
qui transforme un e-mail décrit à partir de composants \entity{Handlebars} ou \entity{Mustache} en du HTML.
|
|
|
|
Après avoir montré ce prototype à mon chef de projet, nous étions tous les deux d'accord que le moteur \entity{Handlebars}
|
|
remplissait mieux nos attentes, en grande partie parce que \entity{Handlebars} permet de passer du code en tant qu'\og enfants \fg,
|
|
tandis que \entity{Mustache} requiet de passer par deux composants (un avant et un après le code \og enfant \fg). \figref{hbs1}
|
|
|
|
\inputfig{hbs1}
|
|
|
|
Pour le premier projet sur lequel \entity{Mint Service} allait être déployé,
|
|
\entity{Amazon SES} \cite{amazonses} allait être utilisé pour faire l'envoi de mails.
|
|
\entity{Amazon SES} permet d'envoyer les e-mails sous format texte et HTML en même temps,
|
|
permettant d'avoir des e-mails facilement lisibles par un lecteur d'écran
|
|
ou sur une messagerie configurée pour afficher les mails sans leur mise en forme.
|
|
|
|
La partie \entity{Handlebars} a donc été étendue pour permettre d'annoter quels parties du code doivent être
|
|
placés dans la version texte du mail. [Figures~\ref{hbs2a} et \ref{hbs2b}]
|
|
|
|
\inputfig{hbs2}
|
|
|
|
\subsubsection{Implémentation initiale dans Mint-Service}
|
|
|
|
Une fois que le prototype me donnait des résultats satisfaisants,
|
|
j'ai pu passer à l'implémentation au propre du système d'envoi de mails dans \entity{Mint Service}.
|
|
Cette première implémentation devait faire l'envoi de mail en deux étapes:
|
|
|
|
\begin{enumerate}
|
|
\item Les mails sont chargés et sont rendus, puis sont mises en lignes sur \entity{Amazon SES} en tant que \og templates \fg \cite{amazonsestemplate}
|
|
\item Les mails sont ensuite envoyés en dirigeant à \entity{Amazon SES} de faire le rendu et l'envoi d'une des \term{templates} à l'adresse mail voulue, avec les données voulues
|
|
\end{enumerate}
|
|
|
|
J'ai d'abords créé et implémenté une librairie permettant de compiler du code \entity{Handlebars}
|
|
ainsi que des composants en format HTML et en format texte, nommée \texttt{handlebars-generator}. Cette librairie ne s'occupe pas du
|
|
chargement des fichiers, ni de l'envoi des mails, et est implémentée sans framework,
|
|
ce qui permet de la réutiliser dans le futur pour d'autres projets.
|
|
|
|
Puis, j'ai créé et implémenté une librairie Nest.JS nommée \texttt{mailing-nest} qui s'occupe de:
|
|
|
|
\begin{itemize}
|
|
\item Charger les composants et les e-mails depuis un dossier donné
|
|
\item Transformer ceux-ci avec \texttt{handlebars-generator} en HTML et en texte
|
|
\item Mettre en ligne des \term{templates} pour utiliser le service d'envoi d'e-mails personalisés d'\entity{Amazon}
|
|
\item Envoyer les \term{templates} avec les informations de l'utilisateur dedans avec \entity{Amazon SES}
|
|
\end{itemize}
|
|
|
|
Afin de pouvoir tester le fonctionnement de la librairie \texttt{mailing-nest},
|
|
j'ai également créé une petite application Nest.JS qui affiche dans le navigateur les e-mails en format HTML et en format texte.
|
|
Cette application permet aussi de développer rapidement les e-mails sans devoir passer par Amazon pour tester
|
|
les modifications.
|
|
|
|
Enfin, j'ai implémenté un module Nest.JS dans l'application \entity{Mint Service},
|
|
qui charge le module de \texttt{mailing-nest} avec en paramètre le chemin des e-mails pour \entity{Mint}.
|
|
Ce module est importé à plusieurs endroits dans le code, et expose des méthodes dans son service permettant
|
|
de convertir les entités de \entity{Mint Service} au format attendu par les e-mails.
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{mailingnest}
|
|
\caption{Organisation initiale du système d'envoi de mail dans Mint-Service}
|
|
\end{figure}
|
|
|
|
Avec le système de mail en place, j'ai pu implémenter la première template d'e-mail, qui était celle pour l'envoi d'identifiants au patients.
|
|
Très rapidement, j'ai pu voir sur \url{canie-mail.com} que la quasi-totalités des fonctionnalités HTML5 modernes n'étaient pas supportés par \entity{Microsoft Outlook},
|
|
ce qui nous forcerait à utiliser des \texttt{<table>} dans l'entièreté des e-mails pour pouvoir supporter \entity{Microsoft Outlook},
|
|
qui est à ce jour le 3ème client e-mail le plus utilisé du monde \cite{oberloemailclients}.
|
|
|
|
Une solution à ce problème est d'utiliser une librairie assurant la compatibilité avec \entity{Outlook}.
|
|
J'ai choisi la librairie \entity{MJML} \cite{mjml}, qui utilise un langage ressemblant au html, qui est ensuite compilé en HTML compatible avec \entity{Outlook}:
|
|
|
|
\inputfig{email}
|
|
|
|
\subsection{Rétrospective sur le système d'envoi de mails}
|
|
|
|
% - [x] parler des désavantages de cette méthode
|
|
% - [x] parler de ce qu'on prévoyait de faire
|
|
% - [~] parler de comment on l'a fait (pour showcase l'avantage du monorepo), et des étapes prises
|
|
% - [x] parler des améliorations futures qui pourront être faites
|
|
% (ajouter plus de providers, simplifier la logique pour choisir les providers, react.e-mail, svelte, etc.)
|
|
% - [ ] Comparer la maintenabilité
|
|
|
|
Après avoir fini l'implémentation du système de mail, j'ai pu l'observer alors qu'il est passé par la vérification de qualité,
|
|
des déploiements et recettes sur un serveur local, et enfin des déploiements chez les clients.
|
|
|
|
Les deux librairies ont vu peu de changements, et ont fonctionné comme voulu.
|
|
L'organisation du code au sein de \entity{Mint Service} a pris deux essais pour arriver à un format satisfaisant:
|
|
le premier essai plaçait l'envoi des mails dans un service dédié à la gestion des utilisateurs (\texttt{GuestModule}),
|
|
mais il s'averrait qu'il fallait assez de code pour préparer les informations des mails qu'il était préférable de déplacer la logique d'envoi des mails dans un module à part, \texttt{MintMailingModule}.
|
|
|
|
Malgré \entity{MJML} et le système de composants, écrire des templates d'e-mails reste plus compliqué à écrire que du code React:
|
|
|
|
\begin{itemize}
|
|
\item La différence de format entre les templates Handlebars/MJML et les composants React fait qu'il est difficile d'appliquer les intuitions valables pour le React lorsqu'on écrit ou réécrit des templates d'e-mails.
|
|
\\
|
|
Ceci est en grande partie dû au fonctionnement de MJML,
|
|
qui requiet une structure stricte dans le code (\texttt{mj-body} $\rightarrow$ \texttt{mj-section} $\rightarrow$ \texttt{mj-column} $\rightarrow$ texte).
|
|
\\
|
|
HTML lui a une structure plus flexible,
|
|
permettant d'imbriquer la pluspart des éléments disponibles dans un ordre arbitraire. \cite{mdncontentcategories}
|
|
|
|
\item Le choix de faire le rendu final des e-mails avec les templates d'\entity{Amazon SES} rend l'écriture des e-mails plus compliquée
|
|
(il faut mixer ensemble deux différents formats handlebars, l'un étant interprété de notre côté, et l'autre étant interprété du côté d'\entityb{Amazon SES} \figref{e-mailsrchbs}).
|
|
\\
|
|
Ce choix rend aussi le débogage de toute erreur lors du deuxième rendu très compliqué,
|
|
car le seul moyen pour accéder aux erreurs d'envoi de mails sur \entityb{Amason SES} est de mettre en place un système
|
|
de transfert de messages, puis de l'activer en envoyant les mails avec un paramètre précis.
|
|
\end{itemize}
|
|
|
|
Pour palier à la deuxième difficulté, j'ai décidé vers la fin de mon stage de modifier l'implémentation du système d'envoi de mails,
|
|
afin de faire l'entièreté du rendu de notre côté, et d'uniquement utiliser \entity{Amazon SES} pour l'envoi pur des mails.
|
|
|
|
J'avais également comme but secondaire de rendre ce système plus robuste,
|
|
en permettant notamment l'insertion de code pour vérifier la validité des données insérées dans les e-mails lorsqu'on est dans un environnement de développement.
|
|
|
|
Les modifications ont été faites dans l'ordre suivant, qui permettait de progressivement appliquer les changements nécessaires:
|
|
|
|
\begin{enumerate}
|
|
\item Suppression de la transformation des balises secondaires (\texttt{[[} et \texttt{]]}) et des options associées
|
|
\item Marquage de différents types comme dépréciés, et ajout de types pour l'envoi pur d'e-mails
|
|
\item Dépréciation des méthodes d'envoi d'e-mail via les templates SES (\texttt{sendTemplatedEmail}), et ajout des méthodes pour l'envoi pur d'e-mails (\texttt{sendEmail})
|
|
\item Modification des templates pour ne plus utiliser les balises secondaires
|
|
\item Modification de l'application d'apperçu des e-mails pour utiliser le nouveau format des informations substituées dans les mails
|
|
\item Modification de \entity{Mint Service} pour utiliser \texttt{sendEmail} au lieu de \texttt{sendTemplatedEmail}
|
|
\item Suppression de \texttt{UploadModule} et de \texttt{sendTemplatedEmail}
|
|
\item Suppression des types inutilisés
|
|
\end{enumerate}
|
|
|
|
À la suite de ces modifications, les e-mails s'affichent de la même manière qu'avant,
|
|
et le système d'envoi de mails est désormais plus simple à utiliser
|
|
et plus simple à étendre avec différents services d'envoi de mail.
|
|
|
|
Je n'ai pas pu implémenter le système de validation des données, par soucis de difficulté d'implémentation:
|
|
il aurait fallu étendre le système de chargement de composants pour également charger du code Javascript,
|
|
et transformer le format des données utilisé par Handlebars en un format facilement vérifiable.
|
|
\\
|
|
Il est par contre maintenant possible d'écrire dans \entityb{Mint Service} des tests pour s'assurer que la logique de transformation des données reste compatible avec les e-mails.
|
|
|
|
À la suite de cette modification,
|
|
je pense qu'il est encore possible d'améliorer de différentes manières ce système d'envoi de mails:
|
|
|
|
\begin{itemize}
|
|
\item En ajoutant plus de tests automatisés dans le système, notamment des tests d'intégration
|
|
\item En rendant les différents modules plus simples d'utilisation
|
|
\item En permettant d'utiliser une syntaxe plus proche de celle de React pour appeler des composants (\texttt{<Composant>} au lieu de \texttt{\{\{>Composant\}\}})
|
|
\item En utilisant un système comme \href{https://react.e-mail/}{React Email} ou \href{https://astro.build/}{Astro} pour écrire les templates en Javascript, ce qui permettrait aussi de faire de la vérification des données et des types.
|
|
\end{itemize}
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{mailingnest-2}
|
|
\caption{Organisation finale du système d'envoi de mail dans Mint-Service}
|
|
\end{figure}
|
|
|
|
\newpage
|
|
\section{Travail sur Mood TV}
|
|
|
|
\subsection{Introduction}
|
|
|
|
\entity{Mood TV} est une application tournant sur les télévisions \og intelligentes \fg produites par \entity{LG}.
|
|
Ces télévisions font tourner le système d'exploitation \entity{WebOS}, et la version pour laquelle nous visons l'application utilise \entity{Chrome 53}.
|
|
|
|
Comme ces télévisions utilisent chrome, notre application peut être programmée entièrement en React,
|
|
à condition de s'assurer que le code javascript soit compatible avec cette version de chrome.
|
|
|
|
Mon travail sur \entity{Mood TV} a été d'optimiser le temps de chargement de la page:
|
|
avant, celle-ci prenait environ 10 secondes à charger,
|
|
et j'ai été laissé en autonomie pour trouver pourquoi le temps de chargement est si haut et comment le baisser.
|
|
|
|
% Présentation de la plateforme, des télés (chrome 53 hehe), mentionner procentric
|
|
% Parler de l'hydration, du rendu, etc.
|
|
|
|
\subsection{Analyse des performances}
|
|
|
|
La première étape avant de pouvoir optimiser les performances de l'application est de mesurer ses performances.
|
|
Il faut pour cela mesurer de manière précise le temps moyen de chargement,
|
|
et il faut pouvoir avoir un aperçu de quelles opérations prennent le plus de temps.
|
|
|
|
J'ai tout d'abords activé le mode de débogage sur la télévision,
|
|
qui peut être fait via l'API propriétaire de contrôle de la télévision.
|
|
Avec ce mode de débogage, on peut se connecter depuis une interface web aux outils de développeur du navigateur tournant sur la télévision.
|
|
|
|
J'ai ensuite pu prendre plusieurs profils de performance pour observer quelles opérations prenait le plus de temps:
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{mood-tv-before.png}
|
|
\caption{Profil du chargement de la page, avant toute optimisation; annoté}
|
|
\label{moodtvbefore}
|
|
\end{figure}
|
|
|
|
On peut séparer le temps de chargement en différentes étapes:
|
|
|
|
\begin{enumerate}
|
|
\item Le chargement initial (en bleu), qui est le moment durant lequel le navigateur télécharge le code javascript,
|
|
et durant lequel \entity{webpack} prépare ce code pour reproduire un environnement modulaire moderne.
|
|
\item L'\term{hydration} \cite{hydration}, qui est une étape du rendu avec React: cette étape du chargement permet à React d'attacher
|
|
son arborescence d'état au HTML existant, permettant d'intéragir dans le code React avec les éléments du DOM.
|
|
\item La réception des \og catégories \fg, qui consiste au traitement de deux requêtes faites à l'API sur laquelle se trouve le \term{Content Management System}
|
|
\item L'envoi de requêtes pour récupérer les informations des films présents dans ces catégories
|
|
\\
|
|
On peut déjà observer sur cette capture d'écran que cet envoi est en triple, alors qu'il ne devrait n'y avoir qu'une vague.
|
|
\item La réception des informations de films, et le traitement du résultat de ces requêtes.
|
|
\end{enumerate}
|
|
|
|
Ensuite, il me fallait un moyen de mesurer de manière précise les performances avant de pouvoir faire des changements.
|
|
La solution que j'ai trouvé a été de créer et d'implémenter un protocole d'envoi et de réception de performances: \figref{moodtvperf}
|
|
|
|
\begin{itemize}
|
|
\item La télévision mesure les performances avec l'API javascript \texttt{Performance} \cite{performanceapi},
|
|
puis envoie ces valeurs via une requête sur un petit serveur sur mon ordinateur.
|
|
|
|
\item Ce serveur stoque ensuite toutes les valeurs reçues dans un fichier,
|
|
et peut relire de ce fichier si jamais il faut reprendre la mesure de performance.
|
|
|
|
\item Un programme lit les valeurs du fichier, puis calcule la moyenne, l'écart standard de ces valeurs.
|
|
Ce programme peut aussi lire deux fichiers, et comparer les performances entre ceux-ci.
|
|
\end{itemize}
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{moodtvperf}
|
|
\caption{Protocole d'envoi et de réception de performances}
|
|
\label{moodtvperf}
|
|
\end{figure}
|
|
|
|
Les valeurs obtenues sont sous la forme de listes de nombres, une liste par métrique mesurée.
|
|
Soit $(\omega_1, \omega_2, ..., \omega_n)$ une telle liste.
|
|
Si on suppose que ces valeurs correspondent à différents résultats d'une même expérience $X$,
|
|
alors on peut calculer la moyenne empirique ($\mu^{\star}_n$) et la variance ($(\sigma^{\star}_n)^2$) empirique de ces valeurs de la manière suivante:
|
|
|
|
\begin{align*}
|
|
\mu^{\star}_n &= \frac{\sum_{i=1}^{n} \omega_i}{n} \\
|
|
(\sigma^{\star}_n)^2 = S_{n-1}^2 &= \frac{\sum_{i=1}^{n} (\omega_i - \mu^{\star}_n)^2}{n-1} \\
|
|
&= \frac{(\mu^{\star}_n)^2}{n-1} - \frac{\sum_{i=1}^{n} \omega_i^2}{n(n-1)} \\ \\
|
|
\mu^{\star}_n \xrightarrow{n\rightarrow\infty} \mu & \quad \quad \quad \sigma^{\star}_n \xrightarrow{n\rightarrow\infty} \sigma
|
|
\end{align*}
|
|
|
|
Afin de pouvoir comparer différents ensembles de valeurs,
|
|
je fais l'hypothèse que les valeurs suivent une distribution normale ($X \hookrightarrow \mathcal{N}(\mu, \sigma^2)$).
|
|
|
|
J'ai décidé d'utiliser dans mon programme de statistique un test rudimentaire de normalité,
|
|
également présent dans la librairie populaire de benchmark pour le langage \entity{Rust} nommée \entity{Criterion}, \cite{criterion}
|
|
qui m'a permis de détecter une erreur dans la mesure de donnée (celle-ci mesurait la même donnée plusieurs fois).
|
|
|
|
Ce test de normalité vérifie simplement que plus de 95\% des valeurs mesurée tombent dans l'intervalle
|
|
$\mu^{\star} \pm \sigma \cdot z_{0.975}$.
|
|
Comme $\sigma$ n'est pas connu, je majore $\sigma^{\star}$ par la valeur haute de l'approximation \cite{tapprox} de son intervalle de confiance. \figref{normalitytest}
|
|
|
|
Enfin, on peut utiliser le t-test sur les mesures pour estimer si deux variables aléatoires différentes $X$ et $Y$ diffèrent bien de moyenne. \cite{ttest}
|
|
Avec ce test, il est alors possible de déterminer si les performances se sont améliorées ou non.
|
|
|
|
Pour \entity{Mood TV}, les valeurs mesurées sont les suivantes, en millisecondes:
|
|
|
|
\begin{itemize}
|
|
\item Le temps jusqu'au début de l'hydration: \textbf{hydration}
|
|
\item Le temps jusqu'à la première exécution des \texttt{useEffect}: \textbf{mount}
|
|
\item Le temps jusqu'à ce que l'entièreté des requêtes pour charger les informations des films soient finies: \textbf{ready}
|
|
\end{itemize}
|
|
|
|
\inputfig{perf1}
|
|
|
|
% Screenshot du panel des performances sur mood tv (avant et après)
|
|
% Parler de la loi du milieu, du Z-test, du test de normalité, de comment récupérer ces données
|
|
|
|
\subsection{Optimisation du temps de chargement}
|
|
|
|
% - [x] Optimisation du nombre de requêtes faites (avec un dessin? ~ plus tard)
|
|
% - [x] Optimisation de la taille du bundle
|
|
% - [x] Optimisation du parsing des requêtes
|
|
% - [x] Avoir de jolis nombres
|
|
% - [ ] tableau final avec toutes les mesures
|
|
|
|
\subsubsection{Optimisation du nombre de requêtes}
|
|
|
|
La première optimisation a été d'éviter que les requêtes pour récupérer les informations sur les films ne soient envoyées plusieurs fois.
|
|
La fonction pour faire la requête pour une catégorie (\texttt{getItems}) utilisait un \texttt{useCallback} et un cache géré par React,
|
|
ce qui faisait que dès que les informations d'un film était récupéré et stocké dans le cache, React assignait une nouvelle référence à \texttt{getItems},
|
|
causant le \texttt{useEffect} responsable pour charger l'ensemble des informations des films à s'exécuter à nouveau,
|
|
et de nouvelles requêtes sont envoyées en conséquence. \figref{fromeffectloop}
|
|
|
|
Pour résoudre ce problème, j'ai créé une nouvelle fonction qui récupère l'entièreté des informations des films,
|
|
et qui seulement une fois que toutes les requêtes soient finies stoque ces informations dans le cache.
|
|
|
|
\inputfig{perf2}
|
|
|
|
\subsubsection{Optimisation de la taille du bundle}
|
|
|
|
Ensuite, j'ai optimisé la taille du bundle javascript, pour passer de $350$ kilooctets à $243$ kilooctets,
|
|
en enlèvant les librairies qu'on n'utilisait peu et qui prenaient beaucoup de place, comme \texttt{browserify-crypto}.
|
|
|
|
Réduire la taille du bundle permet de réduire le temps passé à télécharger le code javascript de la page,
|
|
et le temps passé à compiler ce code et à charger les différents modules avec \entity{webpack}.
|
|
|
|
\inputfig{perf3}
|
|
|
|
\subsubsection{Optimisation du traitement des réponses}
|
|
|
|
Enfin, j'ai optimisé la logique de traitement des réponses des requêtes pour récupérer les informations sur les films.
|
|
Ce traitement, jusqu'à ce moment, se faisait grâce à la librairie \texttt{class-transformer} \cite{classtransformer},
|
|
qui permet de facilement transformer des objets ordinaires en instances de classe.
|
|
|
|
Cette librairie est utilisée pour transformer les résultats des requêtes, qui sont sous forme d'objets ordinaires,
|
|
en instances de classes proposant des fonctions pour facilement traiter les informations contenues dedans.
|
|
Malheureusement, \texttt{class-transformer} ajoute beaucoup de temps de calcul, qui sur le hardware de la télévision connectée,
|
|
se traduit en secondes de chargement en plus. \cite{classtransformerbenchmark}
|
|
|
|
J'ai donc remplacé \texttt{class-transformer} par une transformation manuelle pour les listes de films,
|
|
qui copie manuellement les données des films et des catégories en instances de classe.
|
|
|
|
Avant de faire ce changement, j'ai d'abords écrit des tests unitaires pour vérifier le comportement du système déjà en place.
|
|
Puis, j'ai progressivement modifié les fonctions de traitement de requête affectées pour utiliser ma transformation manuelle à la place.
|
|
|
|
% Les seuls tests présents auparavant étaient des tests d'intégration, qui avaient cassé au fil des mois et qui ne fonctionnent plus ajourd'hui.
|
|
% J'ai donc dû nettoyer les tests d'intégration existants pour les faire tourner séparemment des tests unitaires.
|
|
|
|
\inputfig{perf4}
|
|
|
|
\subsection{Résultats et rétrospective}
|
|
|
|
Avec ces optimisations, la page prend désormais entre 4 et 6 secondes pour charger, contre 10 à 15 secondes auparavant.
|
|
Le temps d'exécution sur le processeur de la télévision n'est aussi plus un engorgement,
|
|
et on peut voir sur le profil des performances qu'une bonne partie du temps est passé à attendre que les différentes requêtes se finissent. \figref{moodtvafter}
|
|
|
|
\begin{figure}
|
|
\includegraphics[width=\textwidth]{mood-tv-after.png}
|
|
\caption{Profil du chargement de la page, après les optimisations; annoté}
|
|
\label{moodtvafter}
|
|
\end{figure}
|
|
|
|
Faire ce travail m'a permis d'avoir une compréhension plus profonde sur différents sujets:
|
|
|
|
\begin{itemize}
|
|
\item Les techniques d'optimisation logicielle (profilage, benchmarking)
|
|
\item Le modèle de propagation de mises à jour de React (\texttt{useEffect}, les boucles d'évènements infinies \figref{fromeffectloop})
|
|
\item L'optimisation de la taille des bundle javascript
|
|
\item L'extraction de données de performances depuis le navigateur (web vitals \cite{webvitals}, \texttt{reportWebVitals} sur Next.JS \cite{nextjsvitals})
|
|
\item L'analyse de données de performances (z-test, t-test, intervalle de confiance)
|
|
\end{itemize}
|
|
|
|
% TODO: a section about
|
|
% https://gitlab.com/interactive-mobility/front-end/web/moment-entertainment-portal/-/merge_requests/1007
|
|
% https://gitlab.com/interactive-mobility/moment-care/moment-care-web/-/merge_requests/1115/commits
|
|
% https://gitlab.com/interactive-mobility/front-end/web/moment-entertainment-portal/-/merge_requests/1008/commits
|
|
|
|
\newpage
|
|
\section{Conclusion}
|
|
|
|
\subsection{Synthèse des résultats}
|
|
|
|
J'ai été globalement satisfait du travail que j'ai pu fournir durant ce stage:
|
|
les résultats attendus étaient obtenus (le système d'envoi de mail marche encore au moment où j'écris ce rapport, la télévision charge de manière consistante en moins de 5 secondes, etc.)
|
|
et j'ai pu écrire des tests unitaires pour une bonne partie de mon code, ce qui permettra de maintenir sa fonctionnalité dans les années à venir.
|
|
|
|
\subsection{Appréciation des technologies utilisées}
|
|
|
|
J'ai eu durant ce stage des sentiments partagés \entity{React}:
|
|
|
|
D'un côté, je trouve que la programmation avec React souffre de la décision de React à être à mi-chemin entre de la programmation réactive \og fine \fg \cite{reactivity}
|
|
et de la gestion manuelle de mise à jour.
|
|
J'ai rencontré beaucoup de bugs causés par des effets de bord du système de React,
|
|
dont certains qui ne pouvaient qu'être résolus en changeant en profondeur l'organisation de plusieurs composants.
|
|
|
|
De l'autre côté, le refactor de \entity{Mood TV} pour extraire ses composants et les placer dans une librairie était très agréable,
|
|
car j'avais le contrôle sur l'organisation que ces composants allaient avoir, et le résultat était très satisfaisant une fois ces composants agencés ensemble.
|
|
|
|
J'ai apprécié la programmation autour de \entity{Nest.JS}:
|
|
le principe d'inversion de contrôle permet d'écrire du code facilement vérifiable et réutilisable,
|
|
ce qui rend la base de code plus agréable à naviguer et utiliser.
|
|
|
|
J'ai enfin été agréablement surpris par \entity{TypeScript}:
|
|
si on active l'ensemble des vérifications du code, alors il y a très peu de chances d'avoir des erreurs de type au runtime.
|
|
Le système de type est assez avancé pour pouvoir concevoir des types qui garantissent des invariantes qui auraient été difficiles à garantir dans d'autres langages typés.
|
|
Écrire des types avancés en TypeScript est presque un art. \figref{types}
|
|
|
|
\subsection{Estimation du gain apporté}
|
|
|
|
Pour donner une estimation du gain que mon travail a apporté à l'entreprise,
|
|
j'estime que j'ai contribué à 10\% du code sur l'ensemble des applications dévelopées pour \entity{Moment Care}.
|
|
Si on estime ensuite que notre équipe contribue à la moitié de la valeur ajoutée par \entity{Moment Care},
|
|
et que \entity{Moment Care} génère environ 200,000€ par an (les bénéfices de l'année 2022 de \entity{Moment Care} étaient de 183,000€),
|
|
alors on obtient une fourchette basse du gain que j'ai apporté, qui s'élève à 10,000€/an pour les quelques années à suivre.
|
|
|
|
Naturellement, ma part de contribution va diminuer, via du code écrit par d'autres personnes dans le futur, et via des refactors de mon code.
|
|
Une rapide analyse du code sur la monorepository indique que la demi-vie du code est de 6 mois; \figref{halflife}
|
|
ceci donne environ 2.9 années d'espérance à mon code, soit une fourchette basse de 30,000€ de gain sur les années à venir.
|
|
|
|
\subsection{Mot final}
|
|
|
|
J'ai beaucoup apprécié ce stage chez \entity{Moment}.
|
|
L'atmosphère de travail était à la fois chaleureuse et professionelle,
|
|
ce qui m'a permis de découvrir et comprendre des dynamiques de cet univers d'entreprise
|
|
que je n'aurais pas autrement observées pendant mes études ou avec les contributions aux projets open-source que je fais.
|
|
|
|
J'ai apprécié avoir la sensation de faire partie de l'équipe de développement,
|
|
et j'ai apprécié que si on m'expliquait quelque chose, ce n'était pas parce que j'avais le statut de stagiaire,
|
|
mais parce que j'avais demandé ou avais besoin d'apprendre cette chose pour progresser.
|
|
|
|
Je serais tenté de revenir travailler chez \entity{Moment},
|
|
si ce n'était pour la vie des grandes villes qui est incompatible avec mes besoins.
|
|
|
|
\newpage
|
|
|
|
\begingroup
|
|
\raggedright
|
|
\bibliographystyle{plain}
|
|
\bibliography{references}{}
|
|
\endgroup
|
|
|
|
\newpage
|
|
\section{Annexes}
|
|
|
|
\inputfig{nestjs1}
|
|
\inputfig{nestjs2}
|
|
\inputfig{email-src-hbs}
|
|
\inputfig{email-src-mjml}
|
|
% \inputfig{ttest}
|
|
\inputfig{fromeffectloop}
|
|
\inputfig{normalitytest}
|
|
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\textwidth]{survival_plot}
|
|
\caption{Graphique de survivabilité du code sur la monorepository}
|
|
\label{halflife}
|
|
\end{figure}
|
|
|
|
\inputfig{types}
|
|
|
|
\newpage
|
|
\makeutbmbackcover{}
|
|
|
|
\end{document}
|