Deployed f382390 with MkDocs version: 1.3.1
This commit is contained in:
parent
80eaf72cf7
commit
aa61c6538e
18 changed files with 130 additions and 121 deletions
|
|
@ -16,11 +16,11 @@ There have been attempts that at least partially address this issue, however, as
|
|||
\label{fig:scope}
|
||||
\end{figure}
|
||||
|
||||
It is interesting to mention that there is a proliferation\footnote{\href{https://xkcd.com/927/}{xkcd.com/927}} of platform/software as a service (PaaS/SaaS) products for deploying AI\footnote{Such as \href{https://mlem.ai/}{MLEM}, \href{https://streamlit.io/cloud}{Streamlit} or any AutoML SaaS platform, for example, \href{https://www.akkio.com/role/software-engineers}{Akkio} as these often have a one-click deployment feature as well.}. At first, these may look intriguing, however, they tend to only focus on getting code easily deployed in the cloud: AI best practices are not prioritised in this setup. Nevertheless, in many cases, it may be a suitable option to use such a service and these can also complement \textit{GreatAI} as illustrated in Figure \ref{fig:scope}: first, the prototype is transformed into a GREAT service and materialised as a common software artifact implementing the best practices. Then, it is either deployed using a deployment SaaS, or by using the organisation's existing software deployment setup.
|
||||
It is interesting to mention that \href{https://xkcd.com/927/}{there is a proliferation} of platform/software as a service (PaaS/SaaS) products for deploying AI\footnote{Such as \href{https://mlem.ai/}{MLEM}, \href{https://streamlit.io/cloud}{Streamlit} or any AutoML SaaS platform, for example, \href{https://www.akkio.com/role/software-engineers}{Akkio} as these often have a one-click deployment feature as well.}. At first, these may look intriguing, however, they tend to only focus on getting code easily deployed in the cloud: AI best practices are not prioritised in this setup. Nevertheless, in many cases, it may be a suitable option to use such a service and these can also complement \textit{GreatAI} as illustrated in Figure \ref{fig:scope}: first, the prototype is transformed into a GREAT service and materialised as a common software artifact implementing the best practices. Then, it is either deployed using a deployment SaaS, or by using the organisation's existing software deployment setup.
|
||||
|
||||
\section{Requirements} \label{section:requirements}
|
||||
|
||||
The best practices (which are referenced throughout the thesis) with which the design is concerned are a subset of those compiled by Serban et al. \cite{serban2020adoption,serban2021practices} and John et al. \cite{john2020architecting}. The core requirements --- set of covered best practices --- for a software solution that has the potential of improving our problem context are presented in the following along with some explanation and clarification of each of them.
|
||||
The best practices (which are referenced throughout the thesis) with which the design is concerned are a subset of those compiled by Serban et al. \cite{serban2020adoption,serban2021practices} and John et al. \cite{john2020architecting}. The core requirements --- set of covered best practices --- for a software solution that has the potential of improving our problem context are presented in the following along with some explanation and clarification for each of them.
|
||||
|
||||
\paragraph{General} Albeit not explicitly in the list of best practices, compatibility is vital in encouraging adoption. Large projects oftentimes end up depending on numerous packages, each of which may impose some restrictions on the code: since these all have to be satisfied simultaneously, this can result in severe constraints.
|
||||
|
||||
|
|
@ -40,17 +40,17 @@ These requirements were chosen stemming from their general importance and potent
|
|||
|
||||
\section{Design principles}
|
||||
|
||||
Before diving into the concrete issues solved, let us detail the principles that should be used for implementing them in the scope of this framework. As implied in Section \ref{section:scope}, the Unix philosophy \cite{ritchie1978unix,salus1994quarter} of software design is followed. Most notably, the design goal that encourages to \textit{write programs that do one thing and do it well.}\footnote{Of course, \textit{write programs to work together} is also very much applicable, since allowing interoperability is one of the core requirements for \textit{GreatAI}.}. Apart from providing a clear and simple picture of the intended use cases for the library, this is also in line with the main notion of \textit{A Philosophy of Software Design} \cite{ousterhout2018philosophy}: APIs should be narrow and deep. A narrow width refers to having a small exposed surface area, i.e. having a small number of functions and classes in the public API. While depth implies each of them accomplishing an involved, complex goal.
|
||||
Before diving into the concrete issues solved, let us detail the principles that should be used for implementing them in the scope of this framework. As implied in Section \ref{section:scope}, the Unix philosophy \cite{ritchie1978unix,salus1994quarter} of software design is followed. Most notably, the design goal that encourages to \textit{write programs that do one thing and do it well.}\footnote{Of course, \textit{write programs to work together} is also very much applicable, since allowing interoperability is one of the core requirements for \textit{GreatAI}.}. Apart from providing a clear and simple picture of the intended use-cases for the library, this is also in line with the main notion of \textit{A Philosophy of Software Design} \cite{ousterhout2018philosophy}: APIs should be narrow and deep. A narrow width refers to having a small exposed surface area, i.e. having a small number of functions and classes in the public API. While depth implies each of them accomplishing an involved, complex goal.
|
||||
|
||||
In a way, the width of an API is the price users have to pay (the effort required for learning it) to use it, while the depth is analogous to the return they get from it. Having to learn little and being provided by a lot of functionality maximises return on investment (ROI), hence, developer experience (DX). The theoretical frameworks presented in \textit{The Programmer's Brain} \cite{hermans2021programmer} provides us with explanations and vocabulary from psychology for arguing about the cognitive aspects of API design. In the following, two of them will be used for detailing the design principles: cognitive dimensions of code bases (CDCB) which is an extension of the cognitive dimensions of notation (CDN) framework \cite{blackwell2001cognitive}, and linguistic antipatterns \cite{arnaoudova2016linguistic}. The former comes with a set of dimensions which describe different (often competing) cognitive aspects of code that influence one's ability to perform certain tasks with it.
|
||||
In a way, the width of an API is the price users have to pay (the effort required for learning it) to use it, while the depth is analogous to the return they get from it. Having to learn little and being provided by a lot of functionality maximises return on investment (ROI), hence, developer experience (DX). The theoretical frameworks presented in \textit{The Programmer's Brain} \cite{hermans2021programmer} provides us with explanations and vocabulary from psychology for arguing about the cognitive aspects of API design. In the following, two of them will be used for detailing the design principles: cognitive dimensions of code bases (CDCB) which is an extension of the cognitive dimensions of notation (CDN) framework \cite{blackwell2001cognitive}, and linguistic anti-patterns \cite{arnaoudova2016linguistic}. The former comes with a set of dimensions which describe different (often competing) cognitive aspects of code that influence one's ability to perform certain tasks on it.
|
||||
|
||||
Linguistic antipatterns provide guidelines for improving consistency and decreasing the false sense of consistency when there is none. Also, choosing the right names for identifiers can help activate information stored in the long-term memory which makes it quicker to comprehend and easier to reason about the code \cite{deissenboeck2006concise}. Finding the most accurate and useful names is harder than it first seems. Accuracy and usefulness are already often competing goals. The more precise the name, the longer and therefore less convenient to use \cite{butler2009relating}. In short, good names are key to good APIs; consciously considering the implications of names should be an integral part of the design process.
|
||||
Linguistic anti-patterns provide guidelines for improving consistency and decreasing the false sense of consistency when there is none. Also, choosing the right names for identifiers can help activate information stored in the long-term memory which makes it quicker to comprehend and easier to reason about the code \cite{deissenboeck2006concise}. Finding the most accurate and useful names is harder than it first seems. Accuracy and usefulness are already often competing goals. The more precise the name, the longer and therefore less convenient to use \cite{butler2009relating}. In short, good names are key to good APIs; consciously considering the implications of names should be an integral part of the design process.
|
||||
|
||||
Nonetheless, simple APIs come at a high technical cost. The library has to implement these in a way that still allows high-performance in production \cite{kleppmann2017designing} and avoids being tied to specific libraries or technologies. Inspiration for the latter may be gained from the ML pipelines of Prado et al. \cite{prado2020bonseyes}: they show that more freedom can be achieved with plug-and-play steps and preconfigured defaults.
|
||||
Nonetheless, simple APIs come at a high technical cost. The library has to implement these in a way that still allows for high performance in production \cite{kleppmann2017designing} and avoids being tied to specific libraries or technologies. Inspiration for the latter may be gained from the ML pipelines of Prado et al. \cite{prado2020bonseyes}: they show that more freedom can be achieved with plug-and-play steps and preconfigured defaults.
|
||||
|
||||
\subsection{Default configuration}
|
||||
|
||||
\href{https://grugbrain.dev/#grug-on-apis}{Existing frameworks oftentimes suffer from the entanglement of numerous levels of abstractions.} Instead of exposing each implementation detail and encouraging users to interact with most of them, many of these could be abstracted away in a more high-level layer. Where configuration may be helpful for advanced users, default values can still be chosen automatically while providing an override option where necessary.
|
||||
\href{https://grugbrain.dev/#grug-on-apis}{Existing frameworks oftentimes suffer from the entanglement of numerous levels of abstractions.} Instead of exposing each implementation detail and encouraging users to interact with most of them, many of these could be abstracted away in a more high-level layer. Even where configuration may be helpful for advanced users, default values can still be chosen automatically while providing an override option where necessary.
|
||||
|
||||
For example, tracing the evaluations and the model versions used in a distributed fashion is very much expected of a trustworthy system. Hence, turning this feature on by default but allowing opting-out from it can result in less scaffolding required from the library's users. It also decreases their up-front cognitive load which by definition flattens the learning-curve \cite{hermans2021programmer}. Similar features can be imagined for providing a service API for the algorithms and for giving feedback, marking outliers, etc.
|
||||
|
||||
|
|
@ -58,17 +58,17 @@ Being \textit{automated} is listed as a requirement but it is imperative to only
|
|||
|
||||
Learning from this, any kind of guessing must be avoided for creating a pleasant API. However, this conflicts with providing defaults for each configuration value. Even if these would be reasonable defaults derived from educated guesses, they are still merely guesses. Nevertheless, if the users were required to specify each configuration option, that would lead to considerably more boilerplate code. This verbosity is captured by the \textit{Diffuseness} dimension of CDCB and, of course, should be minimised.
|
||||
|
||||
To resolve this conflict, \textit{GreatAI} should have recommended values instead of defaults. This can mean a context object (as suggested in \cite{ousterhout2018philosophy}), which contains the result of each design decision that has to be made for a service's deployment. If not configured manually, the recommended values are applied automatically, just like defaults. The values chosen for each parameter must be clearly highlighted. Coming from the library's single responsibility, the number of parameters should not be immense, hence, the user can be expected to comprehend them instead of just being overwhelmed and skimming it.
|
||||
To resolve this conflict, \textit{GreatAI} should have recommended values instead of defaults. This can mean a context object (as suggested in \cite{ousterhout2018philosophy}), which contains the result of each design consideration that has to be made for a service's deployment. If not configured manually, the recommended values are applied automatically, just like defaults. The values chosen for each parameter must be clearly highlighted. Coming from the library's single responsibility, the number of parameters should not be immense, hence, the user can be expected to comprehend them instead of just being overwhelmed and skimming it.
|
||||
|
||||
This way, the library attempts to notify its user about the existence of these decisions but does not force them to manually decide. As a result, no initial configuration is needed for starting out with the library (high \textit{Provisionality}, low \textit{Diffuseness}) and the dependencies are not hidden since they are explicitly highlighted.
|
||||
|
||||
\subsection{Documentation}
|
||||
|
||||
Little value can be derived from software without good documentation; without a doubt, good documentation is a prerequisite for adoption. Documentation comes in many shapes: modern integrated development environments (IDEs) tend to show a popup of a function's description when requested (on mouse hover for instance), at the same time a more comprehensive online manual and example projects are also still expected. But descriptive error messages can be also viewed as documentation.
|
||||
Little value can be derived from software without good documentation; undoubtedly, good documentation is a prerequisite for adoption. Documentation comes in many shapes: modern integrated development environments (IDEs) tend to show a popup of a function's description when requested (on mouse hover for instance), at the same time, a more comprehensive online manual and example projects are also still expected. But descriptive error messages can be also viewed as documentation.
|
||||
|
||||
The library must have quality documentation for all categories. Accordingly, for structuring it, the \textit{Diátaxis} philosophy is preferred \cite{Procida_Diataxis_documentation_framework} which prescribes dividing documentation into 4 parts along 2 axes: practical-theoretical and passive-active consumption. The four quadrants derived from this are: tutorials, how-to guides, reference, and explanation.
|
||||
|
||||
Once again, we might notice two competing interests: the level-of-detail and the length of the documentation. For example, FastAPI\footnote{\href{https://fastapi.tiangolo.com/async/\#concurrent-burgers}{fastapi.tiangolo.com}}, a popular Python web framework, has extensive descriptions and explanations on all topics related to Python's import system, the HTTP protocol, concurrency, deployment, etc. The actual framework's documentation is sprinkled over these very broad topics. This is certainly helpful for beginners to acquire knowledge from a single place. Nevertheless, this high-level of accessibility actually hinders the process of finding the relevant sections (in CDCB, this shows a trade-off between the support of \textit{Searching} and \textit{Comprehension} tasks). My (and Diátaxis') take is that linking to external resources about the library's domain are welcome, but the documentation must have a single responsibility: describing the library itself.
|
||||
Once again, we might notice two competing interests: the level-of-detail and the length of the documentation. For example, FastAPI\footnote{\href{https://fastapi.tiangolo.com/async/\#concurrent-burgers}{fastapi.tiangolo.com}}, a popular Python web framework, has extensive descriptions and explanations on all topics related to Python's import system, the HTTP protocol, concurrency, deployment, etc. The actual framework's documentation is sprinkled over these very broad topics. This is certainly helpful for beginners to acquire knowledge from a single place. Nevertheless, this high-level of accessibility actually hinders the process of finding the relevant sections (in CDCB, this shows a trade-off between the support of \textit{Searching} and \textit{Comprehension} tasks). Diátaxis' take is that linking to external resources about the library's domain are welcome, but the documentation must have a single responsibility: describing the library itself.
|
||||
|
||||
A large portion of software documentations is automatically generated from source code. This has the advantage of always keeping it in sync with code changes, however, it might also signal that the API is too large because it is inconvenient for the developers to document it by hand. Striking the right balance between handcrafted and automatically extracted documentation may be a vital component of good documentation.
|
||||
|
||||
|
|
@ -80,4 +80,4 @@ Subjectively, a key component of good DX is \textit{Progressive evaluation} thro
|
|||
|
||||
At the same time, Python codebases are rarely strictly object-oriented (OO), they are a mix of the functional, data-driven, and OO paradigms. Consequently, relying on classes for grouping related functions is not always desirable. Therefore, it is even more imperative to name similar functions similarly. This helps discoverability and chunking \cite{hermans2021programmer} which amount to quicker comprehension.
|
||||
|
||||
There is one more reason to prefer consistency: humans have a limited short-term memory (STM) \cite{miller1956magical}. Even though flags as function parameters are frowned upon by some \cite{martin2009clean}, they are useful, especially, when configuring libraries. However, if there is no convention for the default value of a flag, clients have to remember the flag's name and initial value at the same time, quickly overloading their STM. Thus, in the codebase, all defaults must be \texttt{False}. Sometimes, it can result in a \textit{disable} prefix which may turn into a double negation, nevertheless, users should not ever encounter this themselves since the doubly-negated version is the default, thus when overriding it, it is only singly-negated. This approach also implies, something may be recommended to be turned on by default.
|
||||
There is one more reason to prefer consistency: humans have a limited short-term memory (STM) \cite{miller1956magical}. Even though flags as function parameters are frowned upon by some \cite{martin2009clean}, they are useful, especially, when configuring libraries. However, if there is no convention for the default value of a flag, clients have to remember the flag's name and initial value at the same time, quickly overloading their STM. Thus, in the codebase, all defaults must be the same, let us say, \texttt{False}. Sometimes, it can result in a \textit{disable} prefix which may turn into a double negation, nevertheless, users should not ever encounter this themselves since the doubly-negated version is the default, thus when overriding it, it is only singly-negated. This approach also implies, something may be recommended to be turned on by default.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue