Software engineering tools and version control
IDEs, CASE tools, documentation, and version control: commits, branches, merges and finding the commit that broke the robot.
Do this lesson in the simulatorImplementation turns the design into data structures and code a computer can run. On a real project this is done by a team, over months, with thousands of changes. Software engineering is the discipline of doing that reliably, and it depends on tools: an IDE for writing and debugging, CASE tools for the whole lifecycle, and version control so a team can change the same code without losing work. At GCSE you met the IDE (F6.6); this lesson adds the tools that make teamwork possible.
The IDE
An integrated development environment puts the tools a programmer needs in one program:
| Feature | How it helps |
|---|---|
| Editor with syntax highlighting, auto-indent and auto-complete | fewer typing and syntax errors, faster writing |
| Error diagnostics | syntax errors are underlined with a message before the program runs |
| Breakpoints | stop the program at a chosen line to inspect it |
| Stepping | run one line at a time and watch the path the program takes |
| Watch windows | show variables' values changing as the program runs |
| Run-time environment and translator | run the program without leaving the editor |
| Refactoring tools | rename a variable everywhere it is used, safely |
Breakpoints, stepping and watches are what find logic errors, the ones that give no error message.
CASE tools
CASE (computer-aided software engineering) tools support the whole lifecycle, not only coding:
- diagramming tools for structure charts, data flow diagrams, class diagrams and entity relationship diagrams, which keep the design consistent with itself;
- code generators that produce skeleton code or database definitions from a design;
- automated testing tools that run the whole test suite after every change (regression testing, lesson A14.5);
- documentation generators that build a reference from the comments and docstrings in the code;
- project management tools that track the backlog, tasks and bugs.
Documentation
Two audiences need documentation:
- Technical (maintenance) documentation is for programmers who will change the system: the design, the purpose and interface of each module, the data structures, and the test plan. Most of it lives in the code as meaningful identifiers, comments and docstrings, where it is most likely to be kept up to date.
- User documentation is for the people who use it: how to install it, how to do each task, what the error messages mean, and troubleshooting.
Code that is modular, consistently laid out to a coding standard, and well named is cheaper to maintain, because most of the cost of software comes after release (lesson A14.1).
Version control
A version control system (VCS), such as Git, records every change to a project's files, who made it, when and why. The files and their history are kept in a repository.
| Term | Meaning |
|---|---|
| Commit | a saved snapshot of the project, with a message saying what changed and why |
| History (log) | the list of commits, so any earlier version can be looked at or restored |
| Diff | the lines added and removed between two versions |
| Revert | undo a commit by making a new commit that reverses it |
| Branch | a separate line of development, so a new feature can be built without disturbing working code |
| Merge | join a branch's changes back into the main branch |
| Merge conflict | two branches changed the same lines differently; a person must decide which to keep |
| Clone, push, pull | copy a repository, send your commits to a shared one, and fetch others' commits |
A diff is how programmers review each other's changes. Python can show one:
import difflib
commit_2 = ['while distance() > 20:', ' forward(50, distance=5)', 'led("green")']
commit_3 = ['while distance() > 20:', ' forward(100, distance=50)', 'led("green")', 'tone(880, 0.3)']
for line in difflib.unified_diff(commit_2, commit_3, "commit 2", "commit 3", lineterm=""):
print(line)
Lines starting - were removed, lines starting + were added, and lines starting with a space are unchanged context. @@ -1,3 +1,4 @@ means lines 1 to 3 of the old version became lines 1 to 4 of the new one.
Why teams use version control
- Nothing is lost: any version can be recovered, so a disastrous change is one command away from undone.
- Accountability: every line can be traced to the commit, the person and the reason. When the robot starts crashing into walls, the history shows which commit introduced it.
- Working in parallel: each developer works on a branch; merging combines the work, and conflicts are flagged rather than one person silently overwriting another.
- Releases: a commit can be tagged as version 1.2, so the exact code on every robot in every school is known.
- Review: changes are proposed, read as diffs, and approved before they are merged.
A typical feature is built like this:
git switch -c beep-on-arrival # make a branch for the feature
(edit, test)
git commit -am "Beep when parked" # snapshot with a message
git switch main
git merge beep-on-arrival # bring the feature into the main line
Continuous integration takes this further: every commit pushed to the shared repository is automatically built and tested, so a change that breaks the tests is found within minutes, by the person who made it.
Finding the commit that broke it
When a bug appears, the history answers "when did this start?". Look at the versions in order and find the first one that has the problem. Git automates this with bisect, which uses a binary search over the commits (lesson A5): with 1000 commits, about 10 tests find the one that broke it.
Task: the commit log
versions is a list of commits in order. Each is a tuple (message, text): message is a string, and text is the whole program at that commit, as one string with lines separated by \n.
Write a function changes(old, new) that takes two such texts and returns a tuple (added, removed). A line counts by its exact text, including its indentation. If a line appears more times in new than in old, the extra appearances count as added; if it appears more times in old than in new, the extra appearances count as removed.
Compare each commit with the one before it; the first commit is compared with the empty string "". For each commit print commit <n>: <message> (+<added> -<removed>), numbering from 1. Then find the first commit whose text contains the string in BUG, and print bug introduced in commit <n>: <message>. The robot stays still.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
versions = [
("first drive", "from bugbot import *\nconnect()\nforward(50, distance=20)"),
("stop at the wall", "from bugbot import *\nconnect()\nwhile distance() > 20:\n forward(50, distance=5)"),
("faster approach", "from bugbot import *\nconnect()\nwhile distance() > 20:\n forward(100, distance=50)\nled(\"green\")"),
("beep when parked", "from bugbot import *\nconnect()\nwhile distance() > 20:\n forward(100, distance=50)\nled(\"green\")\ntone(880, 0.3)"),
]
BUG = "forward(100, distance=50)"
def changes(old, new):
pass
Challenges
- Two students edit the same line of
approach()on different branches. Describe what happens when the second branch is merged, and how it is resolved. - Write
bisect(versions, bad)that finds the first version containingbadby testing the middle version each time, and count how many versions it looks at. - List three things a user guide for the delivery robot must contain, and three things its technical documentation must contain.