\documentclass[aspectratio=169]{beamer} \usetheme{metropolis} \usepackage{lmodern} \usepackage[czech]{babel} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage{graphicx} \usepackage{wrapfig} \usepackage{color} \usepackage{mathtools} \usepackage{hyperref} \usepackage{epstopdf} \usepackage{amsmath} \usepackage{minted} \hypersetup{ colorlinks, citecolor=black, filecolor=black, linkcolor=black, urlcolor=black } \usepackage{pdflscape} \title{Kdy se kód čte jako kniha?} \author{Kar(t)el Kočí} \date{29.2.2020} \begin{document} \frame{\titlepage} \begin{frame}[fragile] \frametitle{Je čitelnost důležitá?} Proč se vlastně zabývat čitelností kódu? \begin{itemize} \item protože kód píšeme jednou, ale čteme mnohem mnohem vícekrát \item protože čitelný kód je verifikovatelný kód \item protože čitelný kód je rozšiřitelný kód \item protože nikdo vám nepřispěje i když to je open-source pokud to nepřečte \item protože zkrátíte review proces \item protože vás budou mít rádi jak aktuální i budoucí kolegové \item protože vás bude mít rádo vaše budoucí já \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Základní koncepty} \begin{itemize} \item Kód má nejenom fungovat, ale hlavně sdělovat myšlenku jak má fungovat \item Každý a i prázdný znak sděluje myšlenku \item Segmentace do celků \item Naznačování důležitosti pomocí pojmenování a omezení přístupu \item Exaktní a trefné pojmenování \item Dodržování stylu jazyka \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Pojmenujte nepojmenované} \begin{minted}[frame=lines]{sh} configure() { sed -i "s/@$1@/$2/g" "$3" dbg "Applied '$1=$2' on $3" } \end{minted} \begin{minted}[frame=lines]{sh} configure() { local variable="$1" local value="$2" local file="$3" sed -i "s/@$variable@/$value/g" "$file" dbg "Applied '$variable=$value' on $file" } \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Nepřehánějte to ale s vatou} \begin{minted}[frame=lines]{sh} get_home() { local user="$1" awk -F : -v "user=$user" '$1 == user { print $7 }' /etc/passwd } \end{minted} \begin{minted}[frame=lines]{sh} get_home() { awk -F : -v "user=$1" '$1 == user { print $7 }' /etc/passwd } \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Nepřipravujte se na třetí světovou} \begin{minted}[frame=lines]{sh} case "$operation" in add) prepare_add "$@" ;; rem|remove) prepare_rem "$@" ;; *) fail "Invalid mode specified:" "$mode" ;; esac echo "Beginning operation: $operation" case "$operation" in add) do_add "$@" ;; rem|remove) do_rem "$@" ;; *) fail "Internal error: Unknown mode:" "$mode" ;; esac \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Jmenujme obecně a exaktně} \begin{minted}[frame=lines]{c} char *read_file(const char *path); \end{minted} je lepší než: \begin{minted}[frame=lines]{c} char *slurp(const char *path); \end{minted} Protože Perl.. \end{frame} \begin{frame}[fragile] \frametitle{Čtenář není kompilátor aneb char není int8\_t} \begin{minted}[frame=lines]{c} char *receive_message(socket_t); \end{minted} \begin{minted}[frame=lines]{c} void receive_message(socket_t, uint8_t **data, size_t *len); \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Pořadí funkcí} \begin{minted}[frame=lines]{sh} _compile() { ... } compile_tools() { _compile tools/compile toolchain/compile; } compile_target() { _compile target/compile; } compile_packages() { _compile package/compile; } compile() { compile_tools compile_target compile_packages } \end{minted} Protože knihu také nečteme od konce \end{frame} \begin{frame}[fragile] \frametitle{Segmenty je lepší pojmenovat} \begin{minted}[frame=lines]{sh} ############################################## print_help() { ... ############################################## operation_add() { ... \end{minted} \begin{minted}[frame=lines]{sh} #Argument parsing ############################ print_help() { ... # Operation ADD ############################## operation_add() { ... \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Segmenty je lepší zapouzdřit} \begin{minted}[frame=lines]{C} struct config *parse_args(int argc, const char **argv) { ... } int main(int argc, const char **argv) { struct config *conf = parse_args(argc, argv); ... } \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Omezte definiční obor} \begin{minted}[frame=lines]{C} char *scan_file(const char *fpath, const char *regexp) { FILE *file; regex_t preg; regcomp(&preg, regexp, 0); ... file = fopen(fpath, "r"); ... fclose(file); return result; } \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Nejlepší kód není třeba dokumentovat?} \begin{minted}[frame=lines]{C} # Parse arguments struct conf *parse_arguments(argc, argv); # Load configuration load_config(conf); # Invoke appropriate handler switch (conf->operation) { \end{minted} Ano, ale to neplatí o API! \begin{minted}[frame=lines]{C} int pop_int(stack_t, int flags); const char *pop_string(stack_t, int flags); \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Dokumentujte globální proměnné} \begin{minted}[frame=lines]{sh} # Possible values: # running: container is running # stopped: container is stopped # none: container does not exists STATE="none" update_state() { STATE="$(container status)"; } access() { case "$STATE" in running) container sh ;; *) fail "Can't access container in state: $STATE" ;; esac } \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Duplicitní dokumentace a změny} S několika úrovněmi dokumentace je snadné aby se neshodovali. \begin{itemize} \item Implementační na úrovni funkce \item Implementační na úrovni API \item Na rozhraní jazyků \item Vývojářská \item Uživatelská \end{itemize} Jak tomu předejít? \begin{itemize} \item Nechte si drobečky (This is user level function, don't forget to update user's docs) \item Dokumentujte na nejvyšší vrstvě a odkazujte se (This function is documented in API doc) \item Navažte testy na dokumentaci (Testy dokumentovaného chování) \item Kopejte kolegy do všech částí těla pokud dokumentaci ignorují \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Deduplikujte kód} \begin{minted}[frame=lines]{c} uing8_t hight = bytes[i] >> 4; hexastring[2*i] = hight < 10 ? val + '0' : val + 'a'; uing8_t low = bytes[i] & 0xf; hexastring[2*i + 1] = low < 10 ? low + '0' : low + 'a'; \end{minted} \begin{minted}[frame=lines]{sh} #define VAL2HEX(VAL) ((VAL) < 10 ? (VAL) + '0' : (VAL) + 'a') hexastring[2*i] = VAL2HEX(bytes[i] >> 4); hexastring[2*i + 1] = VAL2HEX(bytes[i] & 0xf); #undef VAL2HEX \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Zalamujeme argumenty} \begin{minted}[frame=lines]{c} struct wait_id id = event_run_util(events, result_callback, NULL, &result_data, 0, NULL, -1, -1, "cp", "-f", old, new, (const char *)NULL); \end{minted} \begin{minted}[frame=lines]{c} struct wait_id id = run_util(events, result_callback, NULL, &result_data, 0, NULL, -1, -1, "cp", "-f", old, new, (const char *)NULL); \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Použijme popisnější argumenty} \begin{minted}[frame=lines]{sh} lxc launch images:alpine/3.11 myalpine -e -n default \ -p default -s default \end{minted} \begin{minted}[frame=lines]{sh} lxc launch images:alpine/3.11 myalpine \ --ephemeral \ --network default \ --profile default \ --storage default \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Magické konstanty a jejich konstantnost} \begin{minted}[frame=lines]{c} #define TOKEN_BITS 64 #define TOKEN_BYTES 8 bool verify(uint8_t *token) { uint64_t value = *token; return value % 42; \end{minted} \begin{minted}[frame=lines]{c} #define TOKEN_BYTES 8 #define TOKEN_BITS (TOKEN_BYTES * 8) #if TOKEN_BYTES != 8 #error Function verify() is implemented to work only with 8 bytes tokens #endif bool verify(uint8_t *token) { \end{minted} \end{frame} \begin{frame} \Large A jaké máte tipy pro zlepšení čitelnosti kódu vy? \Large Děkuji za pozornost. \url{git.cynerd.cz} \end{frame} \end{document}