Each section below starts collapsed. Use the show / hide (blue) button to show / hide sections.

Moon Lander Game had no Specification ShowHide

If I am building software for a client they (or I) will write a specification. That may be as simple as a set of bullet points, or it may be a 100 plus page document. This is an essential step, so that there can be a shared mutual understanding of what will be delivered.

If you ask me to build you a system, I will not do it without a mutually agreed specification. Even if I write it for you. Which is often the best thing to do. We sit down over a coffee and talk, I take notes. And I prepare enough of a specification to make both of us confident, that I understand your requirements.

Even though Moon Lander Game never had a specification, it did have some initial guidelines:

  • Similar to the traditional Moon Lander Games of the 1970's
  • Run in the browser
  • Minimal hosting cost
  • Fun - That is it is winnable (and you don't need to read a manual to start)
  • Correct - Driven by a real physics engine with real numbers
  • Educational - Education by stealth, learn while having fun, but when you want to know more, the information is available.

As Moon Lander Game did not have a list of requirements. This was not a project that could be estimated, because the scope was unknown. I call this sort of project an exploratory project, we do not know where we are going (or what it is going to cost), until we get there.

For 'normal client projects' a specification is essential. It does not need to be complete, it needs to be complete enough that everyone feels that they know what is needed and they are also confident that there is a common shared understanding across the whole team as to what the requirements are.
There are two real risks here:

  • Overcooked Throw everything we can think of into the specification, there will be paralysis by over analysis, and if you ever finish the specification, then, you have defined a system you can never build.
  • Undercooked Don't worry, you can trust me, I know what you want. Often called Agile development

The goal is the middle ground. This takes mature sensible people, who know what they want, who can communicate with each other and are willing to say: "sorry, I don't understand, please explain that further."

You can be agile, but if the requirements are not written down, you can't be sure that they are mutually understood.
Multiple studies have shown that Misunderstood requirements are the highest risk in software projects.

Test Driven Development (TDD) ShowHide

I am a strong believer in unit testing and test driven development (TDD). But TDD is not always appropriate. TDD requires a specification to write tests against. TDD requires a stable environment.

Unit tests are excellent for certain kinds of confidence: calculations, rules, transitions, edge conditions, deterministic behaviour. Most importantly, has anything unexpected changed since the last release?

As a large fraction of the system is still changing release to release, unit tests would just get in the way at this time. This should change quickly, one of my next tasks will be to write some initial unit tests, probably starting with the physics engine.

In a highly interactive game graphics is a critical part of the user experience. Using a Model-View-ViewModel (or MVVM) with a testable ViewModel is a good way to get many of the benefits of unit testing without directly testing the brittle graphics rendering process.

The user experience still needs an end user review of: layout, responsiveness, rendering behaviour, control feel.

Number of Users ShowHide

Before choosing an architecture, it is worth thinking about what the usage could be. Not because anybody can predict it exactly, but because the shape of the solution should make sense if interest remains modest, and also if interest grows more quickly than expected.

The truth is that I do not know how many people will use this app. Public-facing mass market software can be very hard to predict. Sometimes growth is slow and steady. Sometimes something catches attention at the right moment and traffic jumps sharply for reasons nobody could properly forecast in advance.

Moon Lander Game is aimed at people with an interest in physics, spaceflight, engineering, computing, and classic style gameplay. That is a broad enough audience that usage could remain quite modest, or it could become much larger if the app gets noticed in the right places.

So rather than trying to pretend I know the exact numbers, I have treated this as a design question. The architecture should not depend on a narrow prediction being right. It should be comfortable with light usage, but also able to cope with a quick burst of public interest.

This is one of the reasons for using a browser-based model where the main gameplay runs within the client (within the browser). This will significantly reduce the load on the server. The server still has important work to do, hosting the site, but it does not need to carry the weight of the live game simulation for every player for every tick of the clock.

In short, the goal was not to build for one precise number of users. The goal was to choose an architecture that could start small, stay practical, and still make sense if the audience turned out to be much larger than expected.

Architecture Matters ShowHide

The potential massive number of users provides the first architecture requirement to handle a potentially large number of simultaneous users.

So, we need to minimise ongoing server-side work. A simulation game is a simple loop of:

  • Read user commands
  • Calculate new state
  • Calculate new display properties
  • Update display

That is a very busy loop, that is repeated throughout the gameplay. For it to look realistic, it needs to repeat many (30+) times per second so that it feels more like a movie playing, than a computer screen updating. We cannot afford for the app to be slowed by communications with the server or slow server responses. To minimise ongoing server-side work, the actual game logic must run in the browser after download. This is the basis for the decision to use Blazor / WebAssembly.

The rest of the site can just be normal pages downloaded from the server. But we also need to elastically grow the server resources to meet an elastically growing number of users in real time. This is the basis for the decision to use Cloud based hosting.

So, the base design is:

Project structure matters, I wanted to avoid where code for the game logic was scattered around in a mess of tangled files, When responsibilities start bleeding into each other, everything gets harder: harder to reason about, harder to test, harder to change, and harder to trust. A clean structure does not make a project magically simple. What it does do is stop avoidable confusion from spreading everywhere.

Blazor and WebAssembly (WASM) ShowHide

Once the game is downloaded, the work of running it belongs on the user’s machine, not on my server. For this kind of project, that matters.

I also liked the security model. A browser-based application can still feel much more like a local program than a traditional server-driven page, but it runs inside the browser sandbox. That gives you a useful combination: local responsiveness without the broader access that a traditional installed program might have.

There was also a practical engineering reason. I wanted to use C# throughout as much as possible, and to minimise JavaScript to what was actually necessary rather than letting it spread everywhere. This was the right fit for the way I wanted to think about the project, and a large bit of a personal preference. I know C# well, I am not a JavaScript person. C# is faster and simpler (for me) to program. And I firmly believe that C# is a far better language than JavaScript.

So the choice of Blazor was partly technical, partly architectural, and partly philosophical. I wanted a browser application that still felt like software engineering, not a pile of disconnected web tricks.

Code Quality Matters ShowHide

Good code gets written once, and then maintained for years.

That simple fact changes how I think when I write it. I am not only asking whether the code works today. I am asking whether it will still make sense later. Whether the intent will still be visible. Whether the next person reading it will be able to follow it without reconstructing my thought process from scratch.

And quite often, the next person is me. So, my usual question is this: how would I read this code at 3am if it had just crashed?

At that point, I do not want code that is clever or elegant. I do not want code that saves five lines by hiding its meaning. I want code where the logic stands out. Where few comments are needed because each step is clear, logical variable names are used, steps are separated out into short logical lines.

This way, a human reader can quickly look at a method and be able to see:

  • What it is doing
  • What state it depends on
  • What state it changes
  • Any assumptions it is making
  • Where it could go wrong

Project Managers and Outsourced Development

There is a real problem, when the project manager cares about their current project only. They do not care about the following maintenance project. That is why a lot of poor quality code is written, because maintaining the code is the next guy's job.

This can be a real problem in the software industry, the project manager has a current development project (with a budget). They want to get that project done on time and on budget. The next project will have a new budget / schedule (and likely a new project manager).

Bad project managers can disrespect the true client needs and the total cost of software development. This is where outsourced development can be a real problem because the outsourcing company has no real interest in reducing the long term cost of maintaining the code.

Solution: Same People Maintain the System

One of the best places I have worked had a guideline (rule) that the development programmers and the project manager were the same team who maintained the production systems for the first year. This way, they were also the ones who had to deal with the consequences of their code quality decisions.

Code Style Guides

Watch out for people with code style guides. Large companies and public organisations with huge budgets write style guides for what they need.

What they need and what you need are going to be different. Just because NASA can send someone to the moon, does not mean that I should write my software following their style guide.

The right style you should follow is what works for you today. This will evolve over time, it is worth thinking about what you need to achieve with your code quality goals.

The Browser Is a Real Platform, Not Just a Modern Terminal ShowHide

The browser is much more than a modern form of terminal. It is a real platform in its own right. It has its own strengths, and its own weaknesses, with its own engineering challenges (CSS and JavaScript).

There is a perception that the browser is a modern tool. This is wrong, it uses HTML, CSS and JavaScript for programming. These tools are horrible old kludges, but they are very powerful horrible old kludges.

HTML5 has brought some modern tooling to the browser, specifically Scalable Vector Graphics (SVG) and WebAssembly (WASM), which Moon Lander Game uses extensively.

One of the biggest practical differences is that the browser lives across an enormous range of hardware. The same application may be viewed on a phone, on a mid-sized tablet or laptop, on a normal desktop monitor, or on a desktop with multiple very large high-resolution monitors.

That changes the problem, the app must be responsive to these different environments.

If I had been writing Moon Lander Game for a PC in 1985, I could have assumed one monitor size and I would have been right often enough for it not to cause too much trouble. Today that assumption is hopeless.

In the browser, layout and presentation are part of the engineering problem. You cannot treat them as an afterthought. You have to think about scaling, spacing, touch behaviour, pointer behaviour, visibility, and how the game feels in very different physical contexts.

Performance Measurement ShowHide

When something runs slowly, it is easy to develop opinions about why. Avoid that, many of those opinions will be wrong.

That is another old lesson that came back here. At one point the game was suddenly clearly running far slower than before. I had changed nothing that would explain it. I rolled back all of my changes, I could find nothing wrong, but I still had a slow game.

This was only a problem in the development environment, production was fine.

The useful move was not to guess, it was to measure. Once I had real numbers: 2 frames per second in development, 30 plus frames per second in testing. With the same code running in both environments, the picture became clearer, we clearly had differences in the environment that caused the performance issues.

Without measurement, performance work becomes guesswork. With measurement, at least you have an understanding.

Development, Test and Production Environments ShowHide

How many times have you heard a developer say, it worked on my machine? There is a comforting fantasy that once the code is working in the development environment, the work is done.

This project highlighted the clear need for a separate test environment that is a 100% mirror of production. As the gap between development behaviour and production behaviour was evident.

Continuous Delivery (CD)

A system can feel poor in one environment and be better in another. This is why I have clear control numbers for performance across all environments.

Part of the reason is that the vendors are tuning and improving their tools all the time. This can be a good thing, but it also means that the environment is not stable. This is known as Continuous Delivery (CD) this is a software engineering approach where teams produce software in short, iterative cycles.

CD can be a great approach to some software development, but it does mean that the environment is changing all the time. In a game, does it really matter if the game fails this release, but I get a new release out tomorrow that fixes it?

It matters, end users are not testers!

The affect of this is that frameworks move. The runtimes move. The build process changes. Browser behaviour changes. Hosting behaviour changes. Most of the time, those changes are improvements. Sometimes they are not. And sometimes things that worked one day, stop behaving quite the same way the next.

Protection From Continuous Delivery (CD)

My view on CD is it disrespects the end user experience. Effectively it says: I am going to change the environment on you without warning, and you need to test the changes.

This is why I keep clear separate environments:

  • Development - Where things change quickly - minute to minute
  • Test - Where a small number of users will test code before it is released to production
  • Production - Where the vast majority of users are, which is typically updated about once a week.

Test is a 100% mirror of production. The code should run properly in test before I even think about shipping it to production.

Yes, in many senses, this is really only a vanity project. But my vanity does not want to ship a broken product. If I am going to put something in front of people, I want to know it behaves well in an environment that reflects the real one. That way, I avoid looking bad.

Controls Are Part of the Design ShowHide

A player feels the controls long before they care how the controls were coded. That is worth remembering.

For this game, input behaviour was not just a technical matter. It shapes the whole user experience.

Pointer control, tilt, throttle, keyboard support, pause behaviour, start behaviour, fullscreen ideas, this all fed into whether the game felt fair, teachable, and satisfying.

Controls can be too sharp, too dull, too clever, too awkward, or too obscure. When they are right, users do not spend much time praising them. They just get on with the game. That is usually a good sign.

Simple Presentation Can Still Take a Lot of Work ShowHide

The visible result here is not extravagant: an SVG play area, a spacecraft, terrain, gauges, HUD elements, some text, and some status feedback. Nothing especially spectacular.

And yet getting that to behave well still involved a good number of practical issues: scaling, spacing, text placement, screen size, responsiveness, clutter, readability, and visual balance.

That is one of those things software keeps reminding you of: simple-looking results are often built on top of a lot of complex hidden decisions.

Correct Rules Are Not Always Good Rules ShowHide

The skill levels were a good reminder of this. Once they started changing terrain, pad size, fuel, and handling, the question stopped being only: does this code work?

It became: does this produce the user experience I want?

A rule can be completely consistent and still lead to a poor result. A game can be logically fair and still feel wrong. A feature can work exactly as designed and still not be good enough.

In a game, there is a real difference between getting software just to run and getting software to work well. One of the primary goals of the app is to be fun, a game that you cannot win, is not fun. That is why the difficulty levels were introduced (and I am even thinking of breaking the rules of physics a little, to make the beginner level simpler / more fun).