Decisions#
Decisions with consequences, why they were taken, and what was rejected. Newest last.
Domain, backend and persistence#
- Panache active-record entities
- Liquibase, not Hibernate-managed schema
- The due-date rule applies to filing a task, not to changing one
- The database defines the enums, and the changelogs were squashed to say so
- The schema carries the constraints, and every string has a bound
- Wire identifiers are constants, and a GET's query count is asserted
Authentication#
- Backend-for-frontend authentication
- One OIDC tenant, switched per profile
- Provider availability comes from configuration
- Name and picture are session data, not identity
- Gravatar is asked by the backend, and that buys accuracy rather than privacy
Frontend and user interface#
- The board is the front page; the project description is a link
- A checkbox for done, a quiet marker for in progress
- Editing starts from the menu, and waits for the server
- Due dates are relative words, set from defaults
- Undo instead of a confirmation, and what it costs
- Importance is a dot, and the word is still there
- A task id is checked before it is put in a URL
- Responses are validated against the schema the backend publishes
- Spacing and type are scales, not values
Testing#
- Quarkus Dev Services, not hand-rolled Testcontainers
- Testing sign-in through the real code flow
- Coverage is measured by
quarkus-jacoco, and gates the build - The end-to-end suite contributes no coverage
- Mutation testing, scoped to the tests that do not boot Quarkus
Build, release and dependencies#
- Jib with a pinned Java 25 base image
- sun_checks with documented relaxations
- Apache License 2.0, as a
LICENSEfile only - A release is a git tag, and the version lives nowhere else
- SNAPSHOT dependencies fail every build, not just releases
- Renovate merges the small updates and asks about the large ones
- CodeQL scanning, unfiltered by path
- The runner is pinned, so an image migration is a decision
- Pages deploys only from
main - Misconfiguration scanning is static, in CI, with Trivy — not AWS Config
- SonarCloud runs on
mainonly, and a failed quality gate becomes an issue
Containers and the local stack#
- Compose files under
deployment/docker/ - Compose probes live in scripts, not inline
- The frontend is served by Red Hat's hardened httpd
Deployment on AWS#
- Tearing an environment down is a parameter, not a destroy
- Deploy identities are OIDC roles, scoped to deploying and nothing else
- OpenTofu rather than Terraform
- One AWS account, with IAM roles and resource tags
- What it costs is stated rather than glossed
- PostgreSQL on RDS, destroyed with a final snapshot when idle
- The application authenticates to RDS with IAM, not a password
- The database user is created by a one-off ECS task, run by a person
- The frontend is static on S3 behind CloudFront, with
/api/*on the same distribution - GitHub Actions, not CodePipeline
- Two deployment paths rather than expand-and-contract
- The load balancer stays, and is internal
- Custom hostnames under an existing zone
- Fargate tasks in public subnets, with no NAT gateway
- VPC flow logs go to S3, all traffic, for 30 days
- CloudFront's
/api/*origin comes and goes with the environment; the distribution stays - CloudFront reaches the load balancer over HTTPS with the environment's own name
- ECS-native blue/green, which needed AWS provider 6
- The frontend's deep links are a CloudFront Function, and a release needs no invalidation