Last semester a student in my programming lab at MUBS submitted a neat Flask endpoint he could not explain. The variable names were polished. The docstring sounded confident. When I asked him to draw the request path on the whiteboard, he froze. He had pasted the whole function from an AI chat window the night before.
I am not anti-tool. I use assistants when I write automation for consulting work in Kampala, and I show students how to use them as a junior pair programmer. I am anti-pretending. Syntax was never the hard part of software. Requirements, edge cases, security boundaries, and ownership were. AI changed the speed of the easy part. It did not retire the hard part.
If your faculty is rewriting assessment under Uganda's competence push, I wrote a related classroom piece: AI as a Catalyst, Not a Crutch. For campus policy pressure, see also Ethical AI Governance in Higher Education.

What actually changed in day-to-day coding
Before assistants were good, I spent a chunk of every week looking up exact library signatures and boilerplate. That tax shrank. Today I can sketch a data transfer object, ask for a first draft of serializers, and move faster into the design questions that matter.
What did not shrink:
Deciding what the system should do when mobile money callbacks arrive twice.
Drawing trust boundaries between a public API and an admin API.
Explaining a failure to a non-technical stakeholder at 21:00.
Owning a bug in production when the generator sounded sure and was wrong.
In Nkumba labs I now grade two artefacts on AI-allowed assignments: the code, and a short viva. If you cannot narrate the control flow, you did not finish the learning outcome even if pytest is green.
Specification beats vibes
Vague prompts create vague programs. The skill that rose in value is specification: types, schemas, examples, and failure modes stated up front.
Weak prompt:
Build a login API in Flask.Stronger prompt I model in class:
Write a Flask route POST /api/v1/login
JSON body: {"email": str, "password": str}
On success: 200 with {"access_token": str, "expires_in": 3600}
On unknown email or bad password: 401 with {"error": "invalid_credentials"}
Use Werkzeug password hashing already stored in users.password_hash
Do not log the raw password
Include three pytest cases: success, bad password, missing field
Assume SQLAlchemy User model as given below:
...The second prompt still needs review. It just fails less often in silly ways. OWASP's guidance on access control and injection remains the checklist I want students to keep open while they review generated handlers: start from the OWASP Top Ten overview and drill into the items that match your stack.
Where AI helps, and where humans still carry the bag
Boilerplate and glue: high help. Still read every line before commit.
Unit test drafts: high help for happy paths. Humans must add the nasty cases (double submit, empty list, unicode names, clock skew).
Refactors with a test harness: moderate help. Without tests, it is roulette.
Threat modelling and authZ: low help. Models suggest patterns; they do not know your political and legal context.
Architecture trade-offs: low help when latency, cost, and offline field offices in Uganda collide. That is local knowledge.
On a consulting project we used an assistant to draft Ansible handlers, then rejected half of them because they assumed cloud-init paths our on-prem images did not have. Speed without environment context is just faster mistakes.
Hallucinations and supply-chain weirdness
Large language models predict tokens. They do not "know" your repository. Common failure modes I show live:
Invented packages: a plausible PyPI or npm name that does not exist, or worse, exists as a malicious squat. Always verify the package page before pip install.
Outdated APIs: code that matched a library two major versions ago.
Security anti-patterns: string-built SQL, MD5 password storage, missing authorisation on "internal" routes, debug mode left on.
Concurrency fiction: async code that looks clean and deadlocks under load.
Treat generated code like a pull request from a stranger. Useful. Untrusted.
A four-step loop I accept in coursework and in client repos
Specify: schemas, types, edge cases, non-functional limits (timeouts, max upload size).
Generate: boilerplate, tests, docs stubs only after the contract is clear.
Verify: run formatters, linters, type checkers, and tests. No green pipeline, no merge.
Review: human reads auth, data validation, logging, and secret handling.
# Example verification gate students must run before submission
python -m compileall .
ruff check .
pytest -q
# and be ready to explain any file the assistant touchedRule I write on the board: never commit a line you cannot explain. In industry terms, that is the same bar as a code review comment you would be willing to defend.
How I want students to use assistants without rotting their fundamentals
Write the failing test yourself first when the topic is core (parsing, recursion, SQL joins, socket timeouts).
Ask the model for alternatives, then choose with reasons, not with "it compiled."
Keep a personal notes file of mistakes the model made so you stop relearning the same trap.
Pair AI use with reading real docs: language references, framework docs, and man pages for system tools when you drop to the shell. For Linux diagnostics that still matter when generated scripts fail, keep the helpdesk command toolkit nearby.
Python remains a strong first language for this workflow because students can read what was generated. That is part of why I still point beginners to Why Python Is Still the Best First Language for IT Students.
What I tell working juniors on client teams
Use the tool to draft. Use your judgement to accept. Put secrets in a vault or environment, never in the prompt paste you will forget in a chat history. Do not paste customer databases into a public model. If the client has no AI policy yet, write a one-page rule set before the team freelances fifteen different habits.
When generated code touches authentication, payments, or personal data, require a second human reviewer. That is not bureaucracy. That is how you sleep.
Key takeaways
AI amplifies engineers who can specify and verify. It accelerates debt for those who skip fundamentals.
Prompts with schemas and edge cases beat "build me a feature."
Run tests and read auth code as if a stranger wrote it, because statistically a stranger did.
Classroom credit should include explanation, not only green output.
Local context (power, bandwidth, regulation, business rules) still sits with the human.
The craft did not die. The bottleneck moved. If you train yourself to specify clearly, verify ruthlessly, and own the result, assistants make you faster. If you outsource understanding, they make you fragile. I would rather mark a slightly slower student who can defend every line than a flashy submission that collapses in a viva.