Prompt per il codice e l’automazione
1) Prompting in ambienti di sviluppo (Python, JS, Bash, SQL)
1.1 Pattern di prompt “comando completo”
Usa questo scheletro, adattandolo al linguaggio:
- Contesto: (cosa devo fare e perché)
- Vincoli: (librerie, versioni, complessità, performance)
- Output atteso: (firma funzione, formato, test inclusi?)
- Stile: (commenti, docstring, tipizzazione, lint)
Template breve
Contesto: voglio <obiettivo>.
Linguaggio: <Python/JS/Bash/SQL>.
Vincoli: <librerie/versioni/limiti>.
Output: <file singolo/funzione + test + istruzioni>.
Stile: <docstring/tipi/commenti chiari>.
1.2 Esempi concreti
Python – Funzione + test
Prompt efficace
Scrivi in Python 3.11 una funzione media(lista: list[float]) -> float che ritorna la media arrotondata a 2 decimali. Aggiungi test con pytest, docstring e gestione di lista vuota con ValueError.
Soluzione
def media(lista: list[float]) -> float:
"""
Calcola la media di una lista di numeri con arrotondamento a 2 decimali.
:raises ValueError: se la lista è vuota
"""
if not lista:
raise ValueError("Lista vuota")
return round(sum(lista) / len(lista), 2)
Test pytest
import pytest
from soluzione import media
def test_media_normale():
assert media([1, 2, 3]) == 2.00
def test_media_decimali():
assert media([10.1, 20.2]) == 15.15
def test_lista_vuota():
with pytest.raises(ValueError):
media([])
Perché funziona: firma chiara, eccezione esplicita, test significativi.
JavaScript – Map/Filter/Reduce
Prompt
Scrivi una funzione nomiMaggiorenni(persone) che riceve array di {nome, eta} e ritorna i nomi con eta >= 18. Aggiungi JSDoc e test rapidi con esempi.
Soluzione
/**
* @param {{nome: string, eta: number}[]} persone
* @returns {string[]}
*/
function nomiMaggiorenni(persone) {
return persone.filter(p => p.eta >= 18).map(p => p.nome);
}
// esempi "test-like"
console.assert(JSON.stringify(nomiMaggiorenni([{nome:"A",eta:18},{nome:"B",eta:17}])) === JSON.stringify(["A"]));
console.assert(nomiMaggiorenni([]).length === 0);
Bash – Script sicuro/idempotente
Prompt
Crea uno script Bash POSIX che crei backup/ (se non esiste) e copi tutti i .txt correnti dentro backup/, con messaggi e set -euo pipefail.
Soluzione
#!/usr/bin/env bash
set -euo pipefail
mkdir -p backup
shopt -s nullglob
txts=(*.txt)
if (( ${#txts[@]} == 0 )); then
echo "Nessun file .txt da copiare."
exit 0
fi
cp -- *.txt backup/
echo "Backup completato ($(date))."
SQL – Aggregazioni
Prompt
In SQL standard, conta gli ordini per cliente nella tabella ordini(cliente_id, data, totale), ordina decrescente, includi alias chiari.
Soluzione
SELECT cliente_id, COUNT(*) AS numero_ordini
FROM ordini
GROUP BY cliente_id
ORDER BY numero_ordini DESC;
1.3 Esercizi (con soluzioni)
Esercizio A
Scrivi due prompt per calcolare la somma dei numeri pari 1..100:
- in Python usa
for;
- in JavaScript usa
reduce.
Soluzione (prompt + codice)
-
Prompt Python
Python 3.11. Scrivi funzione somma_pari_1_100() che usa un ciclo for e ritorna la somma dei pari tra 1 e 100. Aggiungi un test pytest.
def somma_pari_1_100() -> int:
s = 0
for n in range(2, 101, 2):
s += n
return s
-
Prompt JavaScript
JavaScript. Crea costante sommaPari calcolata con Array.from e reduce, considerando i pari da 1 a 100. Aggiungi un console.assert.
const sommaPari = Array.from({length: 100}, (_, i) => i + 1)
.filter(n => n % 2 === 0)
.reduce((acc, n) => acc + n, 0);
console.assert(sommaPari === 2550);
2) Refactoring, testing e debug assistiti
2.1 Refactoring guidato
Prompt
Esegui refactoring di questo codice per leggibilità e performance, senza cambiare il comportamento. Aggiungi commenti su cosa hai migliorato e perché. Applica PEP8, tipizzazione, early return.
Esempio (prima → dopo)
Prima
def f(nums):
s=0
for i in range(len(nums)):
if nums[i]%2==0:
s=s+nums[i]
return s
Dopo
from typing import Iterable
def somma_pari(nums: Iterable[int]) -> int:
"""Somma i numeri pari."""
return sum(n for n in nums if n % 2 == 0)
Perché meglio: meno codice, più espressivo, tipizzato, complessità invariata.
2.2 Testing: unit, property-based, golden files
- Unit test (pytest/unittest)
- Property-based (hypothesis): genera casi automaticamente.
- Golden test: confronta output con “snapshot” noto.
Prompt esempio (pytest + hypothesis)
Scrivi test pytest e usa hypothesis per validare che somma_pari sia uguale alla somma dei n tali che n % 2 == 0.
import pytest
from hypothesis import given, strategies as st
from soluzione import somma_pari
@given(st.lists(st.integers(min_value=-1000, max_value=1000), max_size=200))
def test_somma_pari_property(nums):
assert somma_pari(nums) == sum(n for n in nums if n % 2 == 0)
2.3 Debug assistito (tecnica a checklist)
- Riproduci l’errore con un test minimo
- Leggi il traceback/log
- Isola la riga sospetta
- Aggiungi asserzioni e log
- Correggi, poi test di non regressione
Esempio
Bug: “division by zero”
def rapporto(a: float, b: float) -> float:
return a / b
Fix
def rapporto(a: float, b: float) -> float:
if b == 0:
raise ZeroDivisionError("b non può essere zero")
return a / b
3) Istruzioni multi-turno per funzioni complesse
3.1 Strategia “progressive enhancement”
- Turno 1 – MVP: funzionante, senza fronzoli
- Turno 2 – Robustezza: error handling, logging
- Turno 3 – UX/Performance: progress bar, streaming, caching
- Turno 4 – Test & Docs: unit test + README
Caso studio: downloader di file
-
T1 (MVP)
Python. Funzione download(url: str, path: str) che scarica un file via requests e lo salva. Gestisci HTTP base.
-
T2 (Robustezza)
Aggiungi gestione timeout, retry con backoff, dimensione massima, eccezioni specifiche.
-
T3 (UX/Perf)
Aggiungi barra di progresso tqdm con streaming chunked e resume se possibile.
-
T4 (Test & Docs)
Crea test con responses o pytest-httpserver e README con esempi.
3.2 Esercizio (con soluzione)
Obiettivo: fattoriale in tre turni
-
T1 – Base
def fattoriale(n: int) -> int:
res = 1
for k in range(2, n+1):
res *= k
return res
-
T2 – Input negativi
def fattoriale(n: int) -> int:
if n < 0:
raise ValueError("n deve essere >= 0")
res = 1
for k in range(2, n+1):
res *= k
return res
-
T3 – Memoizzazione
from functools import lru_cache
@lru_cache(maxsize=None)
def fattoriale(n: int) -> int:
if n < 0:
raise ValueError("n deve essere >= 0")
if n in (0, 1):
return 1
return n * fattoriale(n-1)
4) Tool interattivi no-code/low-code (HTML+JS)
4.1 Mini-tool: Generatore di password (accessibile e “copiabile”)
Prompt
Crea una pagina HTML+JS con input lunghezza (min 4, max 64), checkbox per maiuscole/minuscole/numeri/simboli, pulsante “Genera” e “Copia”. Nessuna libreria esterna, stile CSS leggero, validazione e ARIA.
Soluzione (snippet completo, pronto-uso)
<!doctype html>
<html lang="it">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Generatore Password</title>
<style>
body{font-family:system-ui,Arial,sans-serif;max-width:720px;margin:2rem auto;padding:0 1rem}
.card{border:1px solid #ddd;border-radius:12px;padding:1rem;box-shadow:0 2px 8px #0001}
label{display:block;margin:.5rem 0}
button{padding:.6rem 1rem;border-radius:10px;border:1px solid #ccc;cursor:pointer}
.row{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}
input[type="text"]{width:100%;padding:.6rem;border:1px solid #ccc;border-radius:8px}
</style>
<div class="card" role="region" aria-labelledby="tit">
<h1 id="tit">Generatore di Password</h1>
<label> Lunghezza:
<input type="number" id="len" min="4" max="64" value="12" aria-describedby="lenHelp">
</label>
<small id="lenHelp">Valori tra 4 e 64</small>
<div class="row" role="group" aria-label="Opzioni caratteri">
<label><input type="checkbox" id="lower" checked> minuscole</label>
<label><input type="checkbox" id="upper" checked> maiuscole</label>
<label><input type="checkbox" id="nums" checked> numeri</label>
<label><input type="checkbox" id="syms"> simboli</label>
</div>
<div class="row" style="margin-top:1rem">
<button id="gen">Genera</button>
<button id="copy" aria-live="polite">Copia</button>
</div>
<label style="margin-top:1rem">Risultato:
<input id="out" type="text" readonly aria-readonly="true">
</label>
</div>
<script>
(function(){
const $ = id => document.getElementById(id);
const pools = {
lower: "abcdefghijklmnopqrstuvwxyz",
upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
nums: "0123456789",
syms: "!@#$%^&*()-_=+[]{};:,.?/|~"
};
function generate(len, opts){
let pool = "";
Object.entries(opts).forEach(([k, v]) => { if(v) pool += pools[k]; });
if(!pool) throw new Error("Seleziona almeno un set di caratteri");
// garantisci almeno un carattere da ogni set selezionato
const selectedSets = Object.entries(opts).filter(([,v])=>v).map(([k])=>pools[k]);
const result = [];
selectedSets.forEach(set => result.push(set[Math.floor(Math.random()*set.length)]));
for(let i=result.length; i<len; i++){
result.push(pool[Math.floor(Math.random()*pool.length)]);
}
// shuffle Fisher–Yates
for (let i=result.length-1; i>0; i--){
const j = Math.floor(Math.random()*(i+1));
[result[i], result[j]] = [result[j], result[i]];
}
return result.join("");
}
$("gen").addEventListener("click", ()=>{
const len = +$("len").value;
if (len < 4 || len > 64){ alert("Lunghezza non valida"); return; }
try {
$("out").value = generate(len, {
lower: $("lower").checked,
upper: $("upper").checked,
nums: $("nums").checked,
syms: $("syms").checked
});
} catch(e){ alert(e.message); }
});
$("copy").addEventListener("click", async ()=>{
const v = $("out").value;
if(!v){ alert("Niente da copiare"); return; }
await navigator.clipboard.writeText(v);
$("copy").textContent = "Copiato!";
setTimeout(()=> $("copy").textContent = "Copia", 1200);
});
})();
</script>
</html>
Project Work – Mini-app “Task Tracker” (AI + codice assistito)
Requisiti
- Aggiungi attività (titolo, priorità)
- Completa/riapri, elimina
- Contatore completati / totali
- Persistenza con
localStorage
- UI responsive, zero dipendenze
Prompt iniziale
Crea una pagina HTML+CSS+JS, un unico file, con lista to-do con campi “titolo” (obbligatorio) e “priorità” (bassa/media/alta). Salva su localStorage. Filtri per “tutte/attive/completate” e contatori live. Aggiungi funzioni pure testabili per le operazioni sulla lista.
Soluzione (file unico, pronto-uso)
Incolla il seguente codice in un file .html e aprilo nel browser.
<!doctype html>
<html lang="it">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Task Tracker</title>
<style>
:root { --gap:.6rem; --radius:14px }
body{font-family:system-ui,Arial,sans-serif;margin:2rem auto;max-width:800px;padding:0 1rem}
.card{border:1px solid #ddd;border-radius:var(--radius);padding:1rem;box-shadow:0 2px 8px #0001}
.row{display:flex;gap:var(--gap);flex-wrap:wrap}
input[type="text"], select{padding:.6rem;border:1px solid #ccc;border-radius:10px;flex:1}
button{padding:.6rem 1rem;border:1px solid #ccc;border-radius:10px;cursor:pointer}
ul{list-style:none;padding:0;margin:.5rem 0}
li{display:flex;align-items:center;gap:.5rem;justify-content:space-between;padding:.5rem;border-bottom:1px dashed #eee}
.title.done{text-decoration:line-through;opacity:.6}
.pill{border:1px solid #ccc;border-radius:999px;padding:.1rem .5rem;font-size:.8rem}
.pill.high{border-color:#f55}
.pill.medium{border-color:#fa3}
.pill.low{border-color:#6a6}
.toolbar{display:flex;justify-content:space-between;align-items:center;gap:.6rem;flex-wrap:wrap;margin:.5rem 0}
.filters button[aria-pressed="true"]{outline:2px solid #444}
</style>
<div class="card">
<h1>Task Tracker</h1>
<div class="row">
<input id="taskTitle" type="text" placeholder="Nuova attività..." aria-label="Titolo attività">
<select id="priority" aria-label="Priorità">
<option value="low">Bassa</option>
<option value="medium">Media</option>
<option value="high">Alta</option>
</select>
<button id="add">Aggiungi</button>
</div>
<div class="toolbar">
<div id="counters" aria-live="polite">0 completate su 0</div>
<div class="filters" role="group" aria-label="Filtri">
<button data-filter="all" aria-pressed="true">Tutte</button>
<button data-filter="active" aria-pressed="false">Attive</button>
<button data-filter="done" aria-pressed="false">Completate</button>
</div>
</div>
<ul id="list" aria-live="polite"></ul>
</div>
<script>
// ---- Modello e funzioni pure (testabili) ----
function createTask(title, priority="low"){
if(!title || !title.trim()) throw new Error("Titolo obbligatorio");
return { id: crypto.randomUUID(), title: title.trim(), priority, done: false, createdAt: Date.now() };
}
function toggleTask(tasks, id){ return tasks.map(t => t.id===id ? {...t, done: !t.done} : t); }
function removeTask(tasks, id){ return tasks.filter(t => t.id!==id); }
function addTask(tasks, task){ return [task, ...tasks]; }
function filterTasks(tasks, mode){
if(mode==="active") return tasks.filter(t => !t.done);
if(mode==="done") return tasks.filter(t => t.done);
return tasks;
}
function countDone(tasks){ return tasks.filter(t => t.done).length; }
// ---- Persistenza ----
const STORAGE_KEY = "task_tracker_v1";
const load = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
const save = (tasks) => localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
// ---- UI ----
let state = { tasks: load(), filter: "all" };
const $ = sel => document.querySelector(sel);
const listEl = $("#list"), countersEl = $("#counters");
function render(){
const shown = filterTasks(state.tasks, state.filter);
listEl.innerHTML = "";
shown.forEach(t => {
const li = document.createElement("li");
const left = document.createElement("div");
const right = document.createElement("div"); right.className = "row";
const chk = document.createElement("input"); chk.type = "checkbox"; chk.checked = t.done;
chk.addEventListener("change", () => updateState({ tasks: toggleTask(state.tasks, t.id) }));
const title = document.createElement("span"); title.textContent = t.title; title.className = "title" + (t.done ? " done" : "");
const pill = document.createElement("span"); pill.className = "pill " + (t.priority==="high"?"high":t.priority==="medium"?"medium":"low");
pill.textContent = t.priority === "high" ? "Alta" : t.priority === "medium" ? "Media" : "Bassa";
left.className = "row"; left.append(chk, title, pill);
const del = document.createElement("button"); del.textContent = "Elimina";
del.addEventListener("click", () => updateState({ tasks: removeTask(state.tasks, t.id) }));
right.append(del);
li.append(left, right);
listEl.append(li);
});
const done = countDone(state.tasks);
countersEl.textContent = `${done} completate su ${state.tasks.length}`;
save(state.tasks);
}
function updateState(patch){
state = { ...state, ...patch };
render();
}
document.getElementById("add").addEventListener("click", () => {
const title = document.getElementById("taskTitle").value;
const priority = document.getElementById("priority").value;
try {
const task = createTask(title, priority);
document.getElementById("taskTitle").value = "";
updateState({ tasks: addTask(state.tasks, task) });
} catch(e){ alert(e.message); }
});
document.querySelectorAll(".filters button").forEach(btn => {
btn.addEventListener("click", () => {
document.querySelectorAll(".filters button").forEach(b => b.setAttribute("aria-pressed","false"));
btn.setAttribute("aria-pressed","true");
updateState({ filter: btn.dataset.filter });
});
});
render();
</script>
</html>
Test manuali rapidi
- Aggiungi 3 task (alta/media/bassa), completa/riapri, filtra per “Attive” e “Completate”.
- Ricarica la pagina: i dati persistono.
- Prova ad aggiungere titolo vuoto: appare l’errore.
Verifica finale (con risposte)
- Prompt per CSV → media colonna → salva risultato
Prompt consigliato
Python 3.11. Leggi un CSV dati.csv con colonna valore, calcola la media e scrivi media.txt con 2 decimali. Usa csv o pandas (specifica entrambe le versioni). Gestisci file mancante e colonna assente. Aggiungi un test con file temporaneo.
Soluzione minima con pandas
import pandas as pd, sys
def media_colonna_csv(path: str, col: str="valore") -> float:
df = pd.read_csv(path)
if col not in df.columns:
raise KeyError(f"Colonna {col} assente")
m = round(float(df[col].mean()), 2)
with open("media.txt","w",encoding="utf-8") as f:
f.write(f"{m}\n")
return m
if __name__ == "__main__":
try:
media_colonna_csv(sys.argv[1], sys.argv[2] if len(sys.argv)>2 else "valore")
print("OK")
except Exception as e:
print(f"Errore: {e}", file=sys.stderr); sys.exit(1)
- Due modi per ottimizzare molti
if annidati
- Guard clauses (early return) e dizionari/lookup tables al posto di
if/elif multipli.
- Strategy pattern / funzioni di dispatch (mappa
chiave → funzione), oppure pattern matching (Python 3.10+ match, TS switch + oggetti).
- Differenza tra debug e testing
- Testing: attività proattiva per verificare che il comportamento sia corretto (prima e durante lo sviluppo).
- Debug: attività reattiva per trovare e correggere la causa di un errore osservato.
Cheat-sheet (rapido)
- Specificare sempre: linguaggio + versione, librerie, formato output, vincoli, test richiesti.
- Iterare: MVP → robustezza → performance → test/docs.
- Chiedere spiegazioni: “Commenta le modifiche, indica complessità, cita alternative”.
- Automazione sicura: log, validazioni, idempotenza (Bash), transazioni/rollback (SQL), test.