I Came to Learn How to Code. I’m Learning How to Think Like an Engineer.
When I started my learning journey at Zone01 Kisumu, a large part of my attention was naturally on code. How do I write this function? Why is this test failing? Why does this JavaScript behave differently from what I
When I started my learning journey at Zone01 Kisumu, a large part of my attention was naturally on code.
How do I write this function?
Why is this test failing?
Why does this JavaScript behave differently from what I expected?
How should I structure this Go package?
Why is my Python solution producing the right output locally but failing the actual tests?
Those questions still matter. But somewhere between debugging projects, reviewing other people's work, having my own work questioned by peers, and moving between Go, Python and JavaScript, I started noticing something.
Writing code is only one part of software engineering.
And in 2026, with AI capable of generating surprisingly good code in seconds, that distinction matters more than it probably ever has.
The question changed
Early in my journey, completing a project often felt like reaching the point where:
It works.
Now I find myself asking a different set of questions:
Why does it work?
What happens when it fails?
What happens when two users do this simultaneously?
Where does this data go?
Who should be allowed to access it?
How difficult will this be to change six months from now?
What happens when another developer inherits this code?
What assumptions did I make without noticing?
That change has probably been one of the most important parts of my experience at Zone01 Kisumu.
We don't learn entirely through the familiar classroom arrangement where someone explains everything first and we reproduce it afterwards.
A significant part of the experience is peer-to-peer.
You build.
You get stuck.
You investigate.
You ask someone.
Someone asks you.
You defend a decision.
You discover that your explanation doesn't make as much sense aloud as it did in your head.
You review somebody else's implementation and occasionally discover a better solution to a problem you thought you already understood.
There is something uncomfortable but useful about that process.
A compiler can tell me that my program is invalid.
A test can tell me that my implementation produces the wrong result.
A peer can ask:
Why did you build it this way?
That question reaches somewhere tests often don't.
Go taught me to appreciate what isn't there
Working with Go has been particularly interesting for me because the language doesn't seem interested in helping me hide complexity behind clever syntax.
It keeps bringing me back to explicit decisions.
What does this function need?
What should it return?
Where should this error be handled?
What belongs in this package?
What shouldn't this package know about?
You can still write terrible Go. A language cannot save an engineer from poor decisions.
But working with it has made me think more deliberately about boundaries.
Consider something as ordinary as registering a user.
At first glance:
func RegisterUser(user User) error {
return db.Create(user)
}
Problem solved?
Not really.
Where is validation?
What happens if the email already exists?
Where is the password hashed?
Should the database layer even receive a plaintext password?
What should the caller receive if insertion fails?
What should be logged?
Which errors are safe to expose to the client?
Suddenly a tiny function becomes a conversation about the system surrounding it.
That is architecture beginning to appear.
Not as boxes on a beautiful diagram, but as decisions.
Python showed me another side of the same problem
Python feels different.
It lets me express an idea quickly, which is extremely useful when exploring a problem.
But that freedom creates another responsibility.
Something being easy to write doesn't automatically make it well designed.
A Python program can become difficult to reason about surprisingly quickly when responsibilities begin leaking into each other.
Database operations appear inside route handlers.
Validation gets repeated everywhere.
Configuration becomes hardcoded.
Business rules end up scattered across unrelated functions.
Then the application grows.
Suddenly changing one thing breaks three others.
The lesson I have been taking from this is not that one language produces better architecture than another.
It is that architecture begins before the framework.
The language gives us tools.
We decide what relationships those tools create.
JavaScript made the boundaries impossible to ignore
JavaScript has given me a different perspective because it has taken me closer to the browser, user interaction, asynchronous behaviour and the frontend/backend boundary.
A button click can look ridiculously simple.
await fetch("/api/profile", {
method: "POST",
body: JSON.stringify(data)
});
But what happens after that request leaves the browser?
Can the client be trusted?
No.
Who validates the data?
What happens when the network disappears halfway through?
What happens if the request is sent twice?
Can one user manipulate the request and update another user's profile?
What if the server returns an error the interface wasn't expecting?
The code responsible for making the request might be five lines.
The engineering problem is much larger.
Then AI entered the room
This is where my learning journey has become particularly interesting.
I am learning software development at a time when AI can produce code at a speed I cannot compete with.
Give an AI system a sufficiently clear prompt and it can generate:
- REST endpoints
- React components
- SQL queries
- authentication middleware
- unit tests
- Docker configurations
- data models
- documentation
Sometimes within seconds.
Initially, I think it is easy for a learner to see that and wonder:
What exactly am I training for?
But the more projects I work on, the less worried I become about competing with AI on typing code.
Because typing code was never the whole job.
Suppose I tell an AI:
Build authentication for my application.
It can produce something that looks convincing.
But I still need to know enough to ask:
Why JWT?
Where is the token stored?
How does logout invalidate it?
What happens when it expires?
How are refresh tokens handled?
Can a normal user access an administrator endpoint?
Are passwords properly hashed?
Can the login endpoint be brute-forced?
What information leaks through error messages?
What happens if the signing secret is exposed?
AI can generate an implementation.
Someone still has to understand the consequences of that implementation.
Working code can still be dangerous code
This has changed the meaning of "working" for me.
Imagine building an application where users upload their identification documents.
The upload works.
The image appears.
The database saves the URL.
The feature is complete.
Except perhaps the generated URL is publicly accessible.
Perhaps changing:
/documents/184
to:
/documents/185
returns another person's document.
The application works.
The architecture doesn't.
Or imagine an online payment flow.
A payment screen appears.
The user completes payment.
The frontend receives:
{
"paymentSuccessful": true
}
and activates the subscription.
Again, everything appears to work.
Until somebody discovers they can send that request themselves.
The ability to generate the code for these features is useful.
The ability to recognize why an apparently functioning implementation is unsafe is engineering.
Peer learning makes this harder to fake
One thing I appreciate about peer-to-peer learning is that understanding becomes visible very quickly.
You can copy syntax.
You can generate code.
You can memorize an explanation.
But when another developer starts asking why decisions were made, shallow understanding has nowhere to hide.
Why this data structure?
Why this endpoint?
Why is this function responsible for both things?
Why didn't you handle this error?
What happens with an empty input?
Why is this variable global?
What happens at scale?
Sometimes I have an answer.
Sometimes I don't.
The second situation is often where the real learning begins.
I don't think the future engineer writes less code
I think the future engineer spends more time understanding what the code means.
AI is changing the economics of producing software.
The cost of generating 100 lines of code is collapsing.
The cost of deploying the wrong 100 lines of code can still be enormous.
That changes what I want to become good at.
I still want to write good Go.
I still want to understand Python deeply.
I still want to become better at JavaScript.
But I also want to understand systems well enough to look beyond the file currently open in my editor.
Where does the data originate?
Where does it travel?
What trusts what?
What can fail?
What needs to scale?
What should remain separate?
What happens when requirements change?
Those are becoming increasingly important questions in my learning journey.
And they lead directly to another subject I once thought belonged mainly to senior engineers:
software architecture.
In the next part of this series, I want to write about how my understanding of architecture is changing—from thinking of it as complicated diagrams and technology choices to seeing it as something much simpler:
the collection of decisions that determine what happens when a project stops being small.
Part 1 of 3 — Learning Software Engineering in the Age of AI
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.