Course page: Lecture 3 of the 2026 Generative Software Engineering course at Nanjing University

1. Once models execute better, software engineering's questions become clearer
Generative AI can already turn many nuisances in daily life and development into executable tasks: describe the real intention, let an agent operate tools and handle the details, then settle one successful run into a reusable Skill. As models improve, the range of such tasks keeps expanding, but "being able to finish" does not automatically mean "doing it right".
The reason is still the open set of spaces in software engineering: Intention is the problem you actually want to solve, Specification is what result counts as meeting the requirement, and Implementation is the concrete code, assets, and runtime environment. When you say only "build an app at a national scale", even experienced people may picture completely different products in their heads; a model can only pick its own reading inside that huge solution space, so the result may well drift from what the person intended.
A model can write code faster on your behalf, and it can handle tasks that used to be harder, such as visual, spatial, and tool operations, but people still need to understand the basic concepts, judge the quality of the output, and write real constraints such as "used by people all over the world" into the requirements. A prototype that only opens on localhost and a product that can be deployed and maintained both have pages; what separates them is whether the project was organized into a complete deliverable.
Software is not an isolated piece of code. Code, documents, requirements, tests, build configuration, and release method together form the information world's projection of a real requirement. A directory becomes an engineering project because the objects inside it can be run, inspected, collaborated on, and evolved together.
2. Git's first principle: keeping a revertible snapshot of the whole project
Early project management often relied on copying directories: homework V1, homework V2, final version, and then emailing the zip archive to someone else. That does solve the question of whether you can go back after a mistake, but once the versions multiply, it becomes hard to answer which files changed, how two copies should be merged, and who introduced the error.
Git turns this plain requirement into a cleaner model: it manages the complete state of a directory tree at one moment rather than a single file, and it lets the project return to any saved state at any time. Git's underlying objects can be understood through three basic types:
| Object | What it stores | Its role in the project history |
|---|---|---|
| Blob | A sequence of bytes, such as the contents of a file | Stores the file itself |
| Tree | File names, permissions, and references to Blobs or child Trees | Stores the directory structure |
| Commit | A reference to a Tree, a parent commit, a commit message, and other metadata | Marks one project snapshot |
This is a persistent data structure. Changing one file does not require copying the whole project; Git creates only the file object that changed, plus the new Trees needed along the path from that file back to the root directory, and the unchanged parts keep referencing the old objects. Even in a large project, a small edit can form a new snapshot with a very small increment.
This also explains what a branch is: a reference pointing at a particular commit rather than a mysterious parallel world. Different branches can start from the same ancestor and each hold its own snapshots; at merge time, Git tries to put the changes from both sides back into one directory tree. If two people changed the same place and the choice cannot be decided automatically, no magic removes the conflict; a person still has to understand the intention behind each change and make the call.
3. Atomic commits are not formalism: they preserve the ability to explain
Git allows committing many files at once, and it allows commit messages that carry no information, but engineering practice does not encourage either. A good commit should be a single, complete, explainable change: fixing one problem, adding one feature, adjusting one configuration item, or completing a self-contained refactor. A commit message should describe what this change did; a line such as "did some cool stuff" gives later readers nothing to work with.
This granularity brings three kinds of value:
- Every snapshot should build and pass the corresponding tests. Putting a state that does not compile into the main history leaves later readers unable to tell the problem itself from a half-finished checkpoint.
- After a problem appears,
git bisectcan binary-search the commit history to locate the change that introduced the error;git blametraces which commit a line of code came from and returns you to the editing context of that moment. - A fix and the commit that introduced the problem can correspond to each other clearly. A large commit that spans thousands of lines and mixes several goals is hard to audit later if it introduces many problems; a series of small atomic commits gives responsibility, rollback, and re-review clear boundaries.
Committing promptly and pushing promptly has one plain meaning as well: it is a remote backup. A damaged device, a wrong operation, or a change in the local environment should not make work that has not been synced disappear outright.
"All generated files should never be committed" is not an absolute rule either. Build outputs, logs, personal editor configuration, operating-system index files, and keys that are still valid usually should not enter the repository: the first few create meaningless diffs or conflicts, and a key can directly cause a security incident. On the other hand, certain lock files or pre-generated header files may be necessary for reproducing the build. What matters is understanding whether each file should be part of the project's shareable state and expressing that judgment through .gitignore, pre-commit checks, and a review process, rather than mechanically running git add ..
4. Agents can replace repeated operations, but not attention
Once an agent can already call Git, fix conflicts, write commit messages, and even rewrite local history, memorizing every command is no longer the most important ability. The larger share of the work is knowing what project management has to solve: which files changed, whether build outputs or sensitive information got mixed in, whether the commit still builds and passes tests, and whether the change stayed inside the agreed scope.
What this asks for is attention placed where it can change the outcome. In a mature project, the length of the README and the arrangement of its links, the directory structure, the granularity of the commit history, and whether the interface offers hints at points of uncertainty all reveal judgments accumulated over long maintenance. Comparing these concrete differences with how you would have done it yourself, then asking why the project's choice is better, can turn browsing a project from passive intake of information into active learning.
AI makes this learning loop shorter: on meeting a new concept you can ask for an explanation right away, on seeing a practice you can ask about its conditions of application and its counterexamples, and on finding a choice you are unsure of you can ask for the trade-offs in consistency, maintainability, and team collaboration. It lowers the cost of exploring and asking, and it does not cancel the responsibility of deciding whether to adopt something once you understand it.
5. Rules belong where they can stop errors
The constraints of an engineering project can be followed by hand, and they can also run automatically when a critical action happens. A Git pre-commit hook can check formatting, file types, keys, and test status before a commit; an agent hook can trigger reminders and checks when a task finishes, when input is needed, or when a certain kind of tool operation happens. Both share one point: they put rules that are easy to forget in the place where they genuinely affect the outcome.
Rules like these need to be specific enough. Consistent naming, commit format, document layout, and file inclusion scope look minor, and they accumulate into a huge comprehension cost as the project grows, as more people collaborate, and as maintenance continues. Conventions can adopt an existing standard such as Conventional Commits, or be written for a project's own needs; what matters most is consistency inside one project and rules that can be automatically checked or explicitly reviewed.
Large models are especially suited to patient, repetitive checking: reading through a change item by item, explaining risks, giving suggested fixes, and trying repeatedly under verifiable conditions. A model optimizes diligently along the goal it was given, so a wrong rule can also steer it in the wrong direction. Writing "every commit must pass the build and the tests" and "no valid keys may be included" clearly forms a more reliable constraint than vaguely asking it to "be as careful as an expert".
6. The Agent Native workflow: narrow the structure first, then widen parallel execution
AI acceleration does not mean architecture can be postponed. Several agents modifying a system that lacks boundaries at the same time only magnifies the chaos faster. The safer order is to discuss the architecture first with both a stronger model and people: define the core interfaces, decide which state should be shared, make into pure functions as much as can be made into pure functions, and then let agents implement inside small, clear scopes.
Once the interfaces and state boundaries are stable, the project can split into several tasks that can each be audited independently. Each task commits in small steps along its own branch; a failed exploration can be discarded safely, and a successful change goes through tests, review, and merge into the mainline. Linear history, rebase, cherry-pick, and merge commits each carry their own trade-offs, and they serve the same goal: letting later people and agents read, at low cost, where a change came from, what it affected, and whether it can be reused safely.
This is also how agent-native software engineering divides the work:
| Work with the highest payoff for people | Work agents are good at |
|---|---|
| Clarify the real intention, define the architecture, interfaces, shared state, and acceptance criteria | Run repeated operations, read changes, generate candidate implementations and commit messages |
| Judge whether the design is still simple, maintainable, and true to product taste | Build, test, check, fix, and iterate inside explicit rules |
| Decide which semantics on both sides of a conflict should really be kept, and choose when to stop a wrong path | Break a task into small steps and keep comparable records of the process |
The faster models get, the less version control and engineering structure look like an outdated burden and the more they look like the infrastructure that coordinates rapid change. A chaotic project cannot become clear on its own through more tokens; clear architecture, atomic changes, and verifiable history are what turn growing execution power into dependable software.
7. The conclusions left by the third lecture
Git's value goes beyond downloading, uploading, or handling conflicts. It offers a basic method for organizing complexity: writing the project as a series of snapshots that can be understood, built, audited, and rolled back. Branches let exploration run in parallel, merges bring successful changes back together, and the commit history makes errors and responsibility traceable.
In an age when agents can complete more and more operations on people's behalf, the emphasis of this method shifts from having typed every command by hand to being able to propose the right engineering constraints. People need to save their energy for intention, architecture, quality standards, and key judgments; agents keep executing, checking, and iterating inside those boundaries. Software engineering has not disappeared because of automation; it is becoming the ability to manage automation itself.
