Tap the team that won the rally. The score changes, and you’re ready for the next serve.
That’s how simple I want it to feel.
Behind that tap, Scorebook has several things to work out. Who was serving? Does service change? Who serves next? Where does the point belong on the scoresheet? What notation goes with it? Did that point end the game?
The whole project now contains more than 50,000 lines across the application, automated tests, and supporting code. We have more than 1,000 automated checks examining everything from individual scoring decisions to people working on the same match from different devices.
Those numbers give you an idea of how much goes into making the interface simple. Here’s what some of that work actually does for the person keeping the book.
One point changes more than the score
Say Home is serving and wins the rally.
When you tap Home, Scorebook adds the point and records the new running score in the current server’s section of the scoresheet. Home keeps service, and the serving order stays where it is. The app also records the event in the match history and checks whether the new score ends the game.
Now suppose Visitor wins the next rally.
The app adds Visitor’s point, marks the end of Home’s service turn, transfers service, and identifies Visitor’s next server from the recorded serving order. It places the point in the corresponding section of the scoresheet and updates the serving indicators.
You don’t have to make those changes separately. You tell the app who won the rally.
There are details within that sequence that matter. The application has to distinguish a team’s first turn serving from its later turns through the lineup. It has to keep the points with the correct serving position as service moves between teams. It also needs enough information to reverse the action if you tapped the wrong side.
That last part matters to me. Correcting the score should also correct the things that changed because of that score.
The app has to understand what the point means
Consider a game being played to 25. At 24–24, the next point makes it 25–24. Scorebook keeps the game open because the leading team hasn’t established a two-point lead.
At 26–24, it recognizes the winner. It records the game’s completion and checks whether that team has also won enough games to win the match.
The deciding game uses a different scoring target. The app determines which game is the decider from the match format selected during setup. That distinction needs to work whether the match is best of three or best of five.
These are examples of rules that have to become actual behavior in the application. They affect when scoring can continue, when a game finishes, and what the bookkeeper sees next.
Other checks happen when you record other actions. During a substitution, for example, Scorebook checks the team’s substitution allowance and whether the entering player has already been assigned to another position in the serving order.
If something looks wrong, the app can flag it and preserve the concern in the record. The official still makes the ruling. The bookkeeper needs to be able to record what actually happened.
Getting those distinctions right takes work. An application that understands a rule also needs to understand how that rule fits into the job at the table.
The symbols need to follow the action
Anyone who has learned to keep the book knows that the notation matters.
A point scored while the libero is serving needs its associated triangle marking. A penalty point needs to be distinguishable from an ordinary rally point. A substitution needs the appropriate S or Sx notation and the player numbers involved.
Scorebook stores information about the action when you enter it. The scoresheet uses that information to determine what to display and where it belongs.
For example, when the scorekeeper has marked the libero as serving, subsequent serving points carry that information into the record. When service changes, the app clears that serving designation so it doesn’t accidentally carry into the next turn.
The bookkeeper doesn’t have to draw the symbol or enter the point again on another screen. But behind the interface, the application still has to preserve the distinction.
We also test whether the result is readable. One of our browser checks records a point with libero serving active, opens the scoresheet, and verifies that the point value remains visible with its marking. That check protects against a real problem where the marking obscured the number.
That’s the level of detail involved. Having the right information stored is only useful if the person reviewing the sheet can actually read it.
Two people can be doing different jobs on the same match
The libero tracker adds another layer.
The scorekeeper and libero tracker share match information, including the score and who is serving, but they maintain different parts of the record. Recording a libero replacement shouldn’t use up an ordinary substitution. A tracker’s entry also shouldn’t disappear because the scorekeeper recorded a point at almost the same moment.
Scorebook keeps those responsibilities separate while bringing the information together for the people viewing the match.
On the libero sheet, it preserves the sequence of replacements at each position. Earlier entries are crossed out, the current entry remains visible, and libero entries receive the corresponding identification. The serving information comes from the scoring record, so the tracker doesn’t have to maintain another independent account of who has the ball.
We test the two roles working at the same time. We also test corrections, refreshing a page, and losing a connection. Those are ordinary things that can happen while people are using the app, so they need to be part of how we check it.
We built an automated testing machine around all of this
With this many connected pieces, I don’t want us relying on someone remembering to manually check everything after every change.
We’ve built a testing system that repeatedly puts Scorebook through specific situations and checks the results.
Some checks are small and focused. Give the scoring logic 25–24 and confirm that the game stays open. Give it 26–24 and confirm that it identifies the winner. Record a point for the serving team and confirm that service doesn’t move.
Developers call these unit tests. They check individual pieces of behavior against an expected answer.
Other tests open the application in a browser and use it. They tap controls, enter substitutions, open the scoresheet, and inspect what appears. Some run separate sessions for a scorekeeper, libero tracker, and viewer to check how their work interacts.
The current suite includes 697 checks focused on the application’s individual parts, 140 checks of the server’s behavior, and 246 browser tests. There are tests for scoring and notation, but also for access permissions, saving, corrections, and working through connection problems.
A useful example came from Undo. We had a bug where undoing a mistaken point restored the score but could leave the next server wrong.
We now have a test that recreates that sequence: record the mistaken point, undo it, then award the point again and check who serves. That particular mistake becomes something the testing system remembers to look for.
That’s how we keep improving it
When we submit a proposed change, an automated process builds the app and runs the checks. You may hear us refer to that process as CI. In practical terms, it means the new version has to go through the testing machine before it moves toward release.
The new feature gets checked alongside behavior that was already working. A change to Undo still has to pass the scoring checks. An adjustment to the substitution screen still has to produce the expected scoresheet entries.
When a check fails, we investigate before moving forward. Sometimes we’ve broken something. Sometimes an intentional change means the test needs to be updated. Either way, we need to understand the result.
Passing tests doesn’t mean we’ll never find another bug. I still use the app, review changes, and listen to feedback. But those repeated checks let us keep developing without manually replaying every known situation each time.
That gives our small team room to take on complicated improvements. We can change something behind the scenes and check whether the familiar actions still behave as expected.
And when you’re at the table, you can keep doing the simple part: tap the team that won the rally and get ready for the next serve.