Sto pensando a come decidere se è meglio incapsulare il mio lavoro dietro nomi di funzioni ben definiti, o esporlo - cosa che aiuterà gli sviluppatori a capire cosa sta succedendo più velocemente? Esiste un nome per lo studio di questo tipo di problema?
In particolare, se eseguo un gruppo di comandi bash alla fine, ma ho una logica significativamente complessa attorno a quei comandi, a che punto ha senso scrivere in un linguaggio di alto livello come Python, anche se questo offusca gli attuali comandi bash eseguiti?
Problema dettagliato
Attualmente sto provando a scrivere uno script di build Jenkins per il mio progetto con circa i seguenti passaggi:
- Estrai il mio codice da github
- Compilare file sass in CSS
- Apri una sottocartella da un altro progetto github
- Comprimi il progetto
- Caricalo in un archivio oggetti con un ID univoco
Sto pensando a come scrivere per essere il più semplice possibile per gli sviluppatori futuri (questo codice non verrà mai visto dagli utenti finali). Probabilmente questi sviluppatori, ma non sicuramente, saranno abbastanza bravi in Python. Avranno sicuramente una familiarità passeggera con la linea di comando, ma probabilmente non conosceranno lo script di bash più complesso.
La prima iterazione di questo script di build era solo una lista di comandi sequenziali, qualcosa del tipo:
git clone [email protected]:username/project.git
git clone [email protected]:username/sub-project.git project/sub-project
sass --update project/css
tar -czf project.tgz project
swift upload my-container project.tgz --object-name=project-'sha1sum project.tgz'.tgz
Tuttavia, questo insieme di comandi è diventato rapidamente più complesso quando ho iniziato a fare cose come clonare il progetto git se non era già lì, altrimenti aggiornarlo - per accelerare la compilazione. Prima che me ne accorgessi, avevo 50 linee e un paio di condizioni condizionali.
Quindi la prima cosa che ho fatto è stata incapsulare queste in funzioni bash, ad es. update_git_dir , quindi il mio script di build è più simile a questo:
#!/usr/bin/env bash
source helper_functions.sh
update_git_dir project [email protected]:username/project.git
build_sass project/css
create_archive project project.tgz
upload_to_swift project.tgz
Questo è un livello di incapsulamento. Ora lo sviluppatore, che avrebbe compreso direttamente i comandi git clone
etc., non può effettivamente vedere cosa sta succedendo. Devono cercare in helper_functions.sh
.
Tuttavia, col passare del tempo mi sono reso conto che molte delle mie funzioni di supporto ora consistevano in più istruzioni condizionali, assegnazioni di variabili e chiamate di funzioni rispetto ai comandi effettivi. Queste dichiarazioni condizionali possono essere piuttosto opache per qualcuno che non ha familiarità con lo scripting bash:
function create_archive {
project_name=${1}
archive_filename=${2}
# Get revision ids
dependencies_requirements_revision=$(cat ${project_name}/sub-project/requirements-revision.txt)
requirements_context=${project_name}/${requirements_file}
requirements_dir=$(dirname ${requirements_context})
if [ "${requirements_dir}" != "${project_name}" ]; then
requirements_context=${requirements_dir}
fi
latest_revision=$(git-revision-hash ${project_name})
...
Così ho iniziato a migrare il mio codice in Python. Così ora il mio script di build assomiglia a questo:
#!/usr/bin/env python
from builders import GitProjectBuilder
builder = GitProjectBuilder(
project_name='my-project',
swift_container='my-container',
git_repository='[email protected]:username/project.git',
sub_project='[email protected]:username/sub-project.git'
)
# Compress and upload
builder.build_sass(directory='css')
builder.get_sub_project(repo='[email protected]:username/sub-project.git')
builder.build_archive(name='archive.tgz')
upload_location = builder.upload_archive_to_swift(archive='archive.tgz')
print upload_location
Ora, quando guardi in builders.py
, è molto più semplice comprendere la logica: le istruzioni% do% di call e le chiamate di funzione sono molto più leggibili, ma ora siamo ancora più lontani dai comandi della shell reale. Nel mio codice Python il modo più simile a cui riesco ad eseguire direttamente i comandi della shell è il seguente:
def build_archive(self, archive):
print subprocess.check_output(
(
'tar --exclude-vcs --create --file '
'{archive_filename}.tar {project_dir}'
).format(
archive_filename=archive_filename,
project_dir=self.project_name
).split()
)
Se lo sviluppatore ha bisogno di capire esattamente quali comandi vengono eseguiti, ora è molto più difficile.
Riepilogo
Quindi, come posso decidere qual è la migliore architettura per massimizzare la trasparenza mentre incapsula la complessità?
Questo problema sembra simile a quando lavoro con l'iniezione delle dipendenze, dove più dipendenze iniettio piuttosto che incapsulato, più il mio codice di inizializzazione diventa complesso e ho un problema simile a disegnare la linea.
Esiste un nome per questo campo di studi?