Writing code is translating an idea into instructions a computer can execute. Building software is deciding which instructions should exist, which constraints matter, which tradeoffs are acceptable, and how the system evolves without collapsing under its own weight. AI is extraordinarily good at the first problem — the second is where software engineering begins.

Code was never the hard part

The questions that actually decide a system — synchronous or queued, exactly-once or at-least-once, tolerance for eventual consistency, retry counts, event ordering, three-hour consumer outages, traffic today vs two years from now — have very little to do with syntax. Language choice matters for fluency, performance, safety, maintainability, and tooling, but it doesn’t answer the fundamental questions. The hard part is choosing the architecture that represents the right set of compromises.

1 problem can have N completely different correct solutions

Two companies can ask for what looks like the same feature and need wildly different architectures: 500 users vs 20 million; three engineers vs two hundred; strong consistency because mistakes cost money vs eventual consistency in exchange for availability; ship in three weeks vs operate for fifteen years; Kafka/Kubernetes/observability vs one app server and a Postgres maintained by four developers. The technically impressive solution for one company is irresponsible for another. The right question is never “what’s the best way to implement X?” but “given these constraints, this team, this business, this budget, these risks, and the expected evolution of the product, what is the most appropriate way to implement X, today?”

AI generates solutions; engineers own tradeoffs

The dangerous tendency is extending the accelerator into delegating engineering judgment itself. Give an AI a requirement and it will design the system, pick the database, and tell you whether to introduce a queue, microservices, caching, CQRS, event sourcing, or Kubernetes — but the existence of an answer does not mean the engineering problem is solved. The context that decides is enormous: conversations with customers, product history, the skills of the team, operational incidents from three years ago, budget, deadlines, contracts, the legacy system nobody wants to touch, the feature a customer will request in six months. Much of it never makes it into documentation, and none of it gets captured by adding another paragraph to a prompt.

There is no architecture without tradeoffs

Every choice spends complexity somewhere: normalize and you gain integrity but complicated queries; denormalize and you gain reads but synchronization complexity; cache and you cut latency but buy invalidation problems; go async and you decouple components but create new failure modes; split microservices and you gain independent deploys but distributed-system complexity; optimize and you lose readability; abstract and you lose understandability. None of it evaluates independently. The job of the engineer is not to make the system work — it’s to decide where complexity belongs.

Algorithmic thinking matters more, not less

Not textbook algorithms — basic rules applied daily. The ten-point set: decompose the problem; identify invariants; understand data flows; choose appropriate data structures; reason about time and space complexity; identify contention; understand failure modes; recognize which operations are expensive and which actually matter; separate essential complexity from accidental complexity; know what should happen when assumptions stop being true. AI can generate ten implementations in the time an engineer used to write one — which makes choosing the right one ten times more important. That’s what boosted productivity really means.

AI-generated code creates a new ownership problem

Producing code and maintaining code are two different economic activities. Generate and merge thousands of lines, tests pass, the feature works — and something critical may be lost. Does the team understand the architecture, the data structures, why a given abstraction exists, the failure behavior, which assumptions the generated implementation depends on? Lines of code are cheap; understanding is expensive. A 3K-line AI pull request is still 3K lines of cognitive surface area, and a passing test suite is not proof of control. Hence the rule: never ship code you do not own — and AI-generated code should not receive a lower standard of scrutiny because it was generated quickly. The open question is whether to review large AI PRs at all, or move in smaller human–AI iterations instead.

Software engineering may actually be getting harder

The paradox: AI makes programming easier while potentially making software engineering harder. We produce more code, faster, than ever — but our capacity to understand systems has not increased at the same rate. The new bottleneck is no longer “how quickly can we write this?” but “how much complexity can this organization actually understand and control?” Productivity should not be measured by how much software we generate, but by how effectively we solve problems while keeping the resulting complexity under control. AI should increase engineering leverage — not replace engineering judgment.