Files
mission-control/DECISIONS.md
T
Stedoss a4c9ac639a
Build & Test / build-backend (push) Successful in 1m6s
Build & Test / build-frontend (push) Successful in 45s
Limit gif example size in doc
It is quite big to view in the preview
2026-07-28 21:50:43 +01:00

7.4 KiB

Decisions

Fill this in before you send your submission back — a few minutes is plenty. It is the artifact we pay the most attention to, and it is what we will dig into together at the next stage.

What I built and why

drawing
  • A new POST endpoint was added to allow for the creation of mission launches. An example curl request for the new endpoint is:
curl --request POST \
  --url http://localhost:5173/api/missions/551ba7ad-ce93-4539-9083-3b0ef337f011/launches \
  --header 'Content-Type: application/json' \
  --data '{
	"scheduledFor": "2019-07-26T00:00:00",
	"status": "pending"
}'
  • The Mission -> Launch relationship is defined as one-to-many. Originally chose to have a one-one relationship, but changed after deciding that there could be more than one launch per mission (and makes the frontend look a little more interesting).
  • For the UI, I chose to have a table within a table, with an accordion element. It seems to work well, however could get a little cluttered if there are more entities related to Missions in the future.
  • I chose to have the form as a pop-over on the table as the form was small enough, and it allows the user to have the context of the Mission they are adding the Launch to, which something like a modal doesn't do as well.
  • Created a very simple CI workflow at .gitea/workflows/build-test.yml which runs on PR and main merges. You can find the runs at https://git.stevenjw.co.uk/StevenJW/mission-control/actions, the latest ones should be green.

Where AI helped, and what I accepted or rejected

I wrote this code by hand (you might be able to tell!), but used AI to remind myself of libraries that I have not used in a while and answer a few questions around testing. I do usually generate a decent amount of code whilst working, however I had a blast doing this and really enjoyed being in my editor for this one!

One interesting snippet I dismissed was around Enum validation in the CreateMissionLaunchRequestValidator. I don't have this in the final code since I let the JSON deserializer do the validation for me now, but AI suggested this for the validation:

public record MyRequest(string Status);

public class MyRequestValidator : Validator<MyRequest>
{
    public MyRequestValidator()
    {
        RuleFor(x => x.Status)
            .Must(status => Enum.TryParse<MyEnum>(status, true, out _))
            .WithMessage("The status field contains an invalid choice.");
    }
}

Which looks ok, but FluentValidation has a builtin way to do this:

public record MyRequest(string Status);

public class MyRequestValidator : Validator<MyRequest>
{
    public MyRequestValidator()
    {
        RuleFor(x => x.Status)
            .IsEnumName(typeof(MyEnum), true)
            .WithMessage("The status field contains an invalid choice.");
    }
}

Always nice to use the builtins, and it's much clearer what is going on too!

What I would do differently with more time

There are definitely many things I would change for next time, or if I was to implement this for a production service.

Backend

Use more robust integration testing for the .NET tests.

Whilst calling the endpoints directly is ok, it doesn't give you the full picture of how the endpoint is called in a real system. Things like the URL is omitted (which is quite big for the /missions/{MissionId}/launch URL I have created), validation is not done on the endpoint directly and other HTTP pipelines are not triggered, such as middleware. The current tests are useful, but I would still maintain a large amount of full integration tests (including using something like TestContainers) to ensure everything is working as expected as clients would be expecting. Also, as mentioned in the ListMissionsTests class, using SQLite as a stub/test database is not really representative of a full DBMS as it does not support all the features that MSSQL does (making it not a great testbed!) - another reason I would use something like TestContainers for a full run-through.

Pagination/caching

When working on these small example datasets it's fine to have all the data being returned at once, but it's not ok when the data gets even a little larger. Caching is also important, whether it's in-memory or external like Redis.

Authentication

Whilst this exercise does say that auth is omitted, it's always necessary in web services.

More restrictive validation

I have implemented very basic validation (I am a big fan of the Validator<T> pattern that fast-endpoints gives!), I think there could be better validation in how dates and launch statuses are submitted. For example, it would be strange to provide a 4 year old date with the Pending launch status. It might be that this is a legitimate request, but it's worth considering to prevent bad data from being inputted.

Moving database calls to a service layer

Not every database call necessarily needs to go in a service, but most will be used in multiple places (for example, checking if a Mission exists), which makes it a good candidate to extract out for better reuse. This also opens the door for mocking the service layer for testing, too - depending on how deep I'd want to go with unit testing.

Have a look at the infra

It's been quite a while since I've worked on Terraform, and I still think it's my preferred IaC platform (having used Ansible for a little bit now) - and I would like to dive back into it. A lot of the infrastructure already looks to be there! I'm also pretty happy to see the SPA be hosted by .NET, really cool.

Add more robust CI/CD pipelines

I am a big fan of CI/CD pipelines. They are a great way to automate checking (ie. make code reviews faster and more focused), and deployment. Every project should have some pipelines, and it's nice to see formatters and linters for both frontend and backend! I have added a very basic one in .gitea/workflows for building, formatting and testing the backend and frontend. You can view the runs at https://git.stevenjw.co.uk/StevenJW/mission-control/actions.

Frontend

Split out the Frontend components a little more

Currently, the frontend components are a little too tightly coupled, and deal with a lot of external behavior (such as data fetching). Allowing the top-level page/component to fetch the data and pass it down (or using a context) allows for data mocking much easier in tests, allows better re-usability, and makes components more focused.

Add more tests

I didn't want to spend too long on the testing, but I would have liked to test features like the form validation more. Unfortunately, I didn't have enough time to split away the query context from the components, so testing is a lot harder.

Add some E2E tests

I'm a big fan of E2E tests, where the backend is running and the frontend is interacting with it. I've used Cypress and Playright previously, and they are both pretty good.

Make it prettier

I wanted to get the basic functionality in there, but there is always room for improvement for the aesthetics.

Make it more accessible

There are a few shortcuts in the frontend that I have made here that make this site slightly less accessible, and I've not really focused on the aria elements either. I've also just picked some colours at random, which doesn't help readability much. We've run lighthouse and accessibility tests before deploying before in our CI, which is quite a nice benefit!