# Cloud application > Take your own idea from a brainstorm to a product running in the cloud. Every node is a step with a prompt for your AI assistant. One road from the idea to a public URL, a database, a frontend, user accounts and a loop for every feature after the first, with branches into product work, build and ship, security, testing and resilience, observability, data and storage, serving and compute. Every technology choice is written down as a decision record, most of them followed by a short route for the option taken. A launch review checks the whole product before you announce it. Audit an existing repository to see which steps it already has. A talent tree on Gradus (https://gradus.kazet.blog). Every step below links to its page as Markdown, with what the step means and the prompt to hand an AI assistant. The map is at https://gradus.kazet.blog/tree/cloud-application and a step's page is at https://gradus.kazet.blog/tree/cloud-application/step/. ## Environment setup - [First commit](https://gradus.kazet.blog/tree/cloud-application/step/first-commit.md): Initialize a git repository, make the first commit and push it to a remote. - [Pick a language](https://gradus.kazet.blog/tree/cloud-application/step/pick-language.md): Decide what the service is written in before the first line of it: your assistant proposes the languages that fit your idea and your experience, you pick one and it goes in a decision record. - [Language toolchain](https://gradus.kazet.blog/tree/cloud-application/step/language-toolchain.md): Set up the language you picked: a pinned toolchain, the strictest useful compiler or type checker settings, a formatter and a hello world that runs with one command. - [Runnable service](https://gradus.kazet.blog/tree/cloud-application/step/runnable-service.md): Add a package manifest with a lockfile and one entrypoint that starts an HTTP server with a health route, reading its port from the environment, so the service runs locally with a single command. - [Continuous integration](https://gradus.kazet.blog/tree/cloud-application/step/ci-pipeline.md): Add a CI workflow that installs, builds and tests on every push, so a broken change never quietly slips through. - [Container image](https://gradus.kazet.blog/tree/cloud-application/step/container-image.md): Write a multi-stage Dockerfile and .dockerignore so the service builds into a small, reproducible image, and build it in CI. - [Choose where to run](https://gradus.kazet.blog/tree/cloud-application/step/choose-cloud.md): Pick the cloud the image will run on. - [First deploy on Google Cloud](https://gradus.kazet.blog/tree/cloud-application/step/deploy-gcp.md): Deploy the image to Cloud Run from CI on every merge to main, authenticated with Workload Identity Federation rather than a key, behind a public HTTPS URL. - [First deploy on AWS](https://gradus.kazet.blog/tree/cloud-application/step/deploy-aws.md): Deploy the image to ECS Express Mode from CI on every merge to main, authenticated with an OIDC role rather than access keys, behind a public HTTPS URL. - [First deploy](https://gradus.kazet.blog/tree/cloud-application/step/first-deploy.md): Prove the deploy from CI: a post-deploy job that calls the public health route, checks it reports the commit just merged and fails the workflow when it does not. ## Product: Idea, users, decisions, feedback - [Brainstorm the idea](https://gradus.kazet.blog/tree/cloud-application/step/product-idea.md): Brainstorm with your assistant until one idea is worth a few weekends: who it is for, what it does in one sentence, what it deliberately does not do and why now, written to docs/product/idea.md. - [Users & their jobs](https://gradus.kazet.blog/tree/cloud-application/step/users-and-jobs.md): Name two or three kinds of user and the job each hires the product for, so every later decision can ask which user it serves. - [The first journey](https://gradus.kazet.blog/tree/cloud-application/step/first-journey.md): Cut the product to one journey a user can finish end to end. - [Project knowledge base](https://gradus.kazet.blog/tree/cloud-application/step/knowledge-base.md): Give the repository a docs/ folder the assistant reads before every task, pointed at from CLAUDE.md, with the product docs inside and a home for decisions. - [Decision records](https://gradus.kazet.blog/tree/cloud-application/step/decision-records.md): Start docs/decisions with one record per technical choice: context, options, decision, consequences. - [Track work in GitHub Issues](https://gradus.kazet.blog/tree/cloud-application/step/tracker-github-issues.md): Use the repository's own issue tracker: labels for the journey steps, one issue per step of the first version, a project board with three columns. - [Track work in Jira](https://gradus.kazet.blog/tree/cloud-application/step/tracker-jira.md): Use Jira: a project for the product, one issue per step of the first version under an epic and a link from the README. - [Roadmap in iterations](https://gradus.kazet.blog/tree/cloud-application/step/roadmap.md): Lay the first journey and what follows into three iterations with a goal each: first deploy, first feature end to end, ready for strangers. - [Name & pitch](https://gradus.kazet.blog/tree/cloud-application/step/name-and-pitch.md): Name the product, check the domain and write the paragraph a stranger reads first. - [Feedback loop](https://gradus.kazet.blog/tree/cloud-application/step/feedback-loop.md): Where the product branch rejoins the road. - [Product analytics](https://gradus.kazet.blog/tree/cloud-application/step/product-analytics.md): Define five events that show whether the first journey gets finished, emit them and read them on one page. - [Consent for analytics](https://gradus.kazet.blog/tree/cloud-application/step/analytics-consent.md): Ask before tracking when the analytics tool needs it: a fair banner, nothing sent before Allow and a privacy page that matches what really happens. - [Pick the next feature](https://gradus.kazet.blog/tree/cloud-application/step/pick-next-feature.md): Choose the next feature from what users said and did, and write a one-page spec before any code. - [Did it work?](https://gradus.kazet.blog/tree/cloud-application/step/feature-review.md): Read the numbers against the spec, decide to keep, change or remove the feature and delete the flag either way. - [Your feature playbook](https://gradus.kazet.blog/tree/cloud-application/step/feature-playbook.md): Write the last three steps into your repository as prompts of your own, so the next feature needs no map. - [Words & links](https://gradus.kazet.blog/tree/cloud-application/step/words-and-links.md): Read every public page as a stranger would: links that work, facts that are true, no placeholders and nothing personal you did not mean to share. - [Launch review](https://gradus.kazet.blog/tree/cloud-application/step/launch-review.md): Before strangers arrive, audit the repository and the live site against everything this map teaches, with evidence for every item and no changes during the pass. - [Launch day](https://gradus.kazet.blog/tree/cloud-application/step/launch-day.md): Choose the link you announce, see it the way the people you announce it to will, then stay close to the product for the first day. ## Build & ship: Config, lint, releases, IaC - [Project structure & config](https://gradus.kazet.blog/tree/cloud-application/step/project-structure.md): Establish a conventional folder structure and a config module that validates environment variables at startup, so new code has an obvious home and a bad deploy fails fast. - [Linting & formatting](https://gradus.kazet.blog/tree/cloud-application/step/lint-and-format.md): Add a linter and formatter with a shared config, run them in CI and on commit, so style never comes up in review. - [Commit conventions & review](https://gradus.kazet.blog/tree/cloud-application/step/commit-conventions.md): Adopt a commit convention, protect the main branch and require review, so every change has a reason and a witness. - [Versioned releases](https://gradus.kazet.blog/tree/cloud-application/step/versioned-releases.md): Tag releases with semantic versions, generate a changelog from commits and publish the container image under that tag. - [Automated dependency updates](https://gradus.kazet.blog/tree/cloud-application/step/dependency-updates.md): Let a bot open pull requests for outdated dependencies and base images, grouped and scheduled, so upgrades stay small and routine. - [Infrastructure as code](https://gradus.kazet.blog/tree/cloud-application/step/infrastructure-as-code.md): Describe every cloud resource in code (Terraform, Pulumi or your cloud's native tool) so environments can be rebuilt from scratch. - [Staging & production](https://gradus.kazet.blog/tree/cloud-application/step/staging-and-production.md): Stand up separate staging and production environments from the same infrastructure code, with promotion from one to the other. - [GCP landing zone](https://gradus.kazet.blog/tree/cloud-application/step/gcp-landing-zone.md): Commit to Google Cloud: a project per environment, Artifact Registry, Cloud Run and a remote Terraform state bucket, with Workload Identity Federation so CI deploys without long-lived keys. - [AWS landing zone](https://gradus.kazet.blog/tree/cloud-application/step/aws-landing-zone.md): Commit to AWS: an account per environment, ECR, ECS Express Mode or ECS on Fargate and a remote Terraform state bucket, with OIDC roles so CI deploys without access keys. - [One-command rollback](https://gradus.kazet.blog/tree/cloud-application/step/rollback.md): Make undoing a release one command, and let a failed post-deploy check undo it by itself, so a bad release costs a minute rather than an evening. - [Reversible migrations](https://gradus.kazet.blog/tree/cloud-application/step/reversible-migrations.md): Change the data in stages that keep the previous release working: expand, backfill, then contract in a later release, so a rollback never meets a schema it cannot read. - [Blue-green releases](https://gradus.kazet.blog/tree/cloud-application/step/blue-green-releases.md): Start each new version beside the live one with no traffic, test it on a private URL, switch traffic in one move and keep the old version ready to take it back. - [Remote config & flags](https://gradus.kazet.blog/tree/cloud-application/step/remote-config.md): Change behaviour without a deploy: one typed list of flags in code with safe defaults, values held in the cloud's config service and a removal date on each. ## Security: Secrets, auth, TLS, input - [Secrets management](https://gradus.kazet.blog/tree/cloud-application/step/secrets-management.md): Move credentials and API keys out of the code and its history into environment variables or a secrets manager, with a checked-in .env.example and a scanner that keeps them out for good. - [Accounts & spending guardrails](https://gradus.kazet.blog/tree/cloud-application/step/account-guardrails.md): Lock every account the product depends on with a second factor and cap what a mistake can cost before strangers arrive. - [Authorization & least privilege](https://gradus.kazet.blog/tree/cloud-application/step/authorization.md): Layer authorization on top of authentication: roles or scopes that decide what an identified caller may actually do. - [TLS & network policy](https://gradus.kazet.blog/tree/cloud-application/step/tls-and-network-policy.md): Enforce TLS everywhere, let the origin answer only to the edge in front of it and restrict network access between services to only what each one needs. - [Security headers](https://gradus.kazet.blog/tree/cloud-application/step/security-headers.md): Tell the browser what the pages may load and who may frame them, with a strict Content Security Policy and the headers around it. - [Rate limiting & WAF](https://gradus.kazet.blog/tree/cloud-application/step/rate-limiting.md): Add rate limiting per caller and a web application firewall in front of public endpoints, so abuse is throttled before it reaches the code. - [Audit logging](https://gradus.kazet.blog/tree/cloud-application/step/audit-logging.md): Record who did what and when for every sensitive action, in a log the application itself cannot edit. - [Input validation](https://gradus.kazet.blog/tree/cloud-application/step/input-validation.md): Validate and sanitise every request body, query and header against a schema before any handler sees it. - [Dependency & image scanning](https://gradus.kazet.blog/tree/cloud-application/step/vulnerability-scanning.md): Scan dependencies and container images for known vulnerabilities in CI and keep a software bill of materials. - [Choose how users sign in](https://gradus.kazet.blog/tree/cloud-application/step/choose-sign-in.md): Decide who holds your users' credentials: this service or a hosted identity provider, weighed against this product and written as a decision record. - [Passwords & sessions](https://gradus.kazet.blog/tree/cloud-application/step/passwords-and-sessions.md): Store passwords with Argon2id through a maintained library and keep users signed in with secure cookie sessions that renew at sign-in. - [Password reset](https://gradus.kazet.blog/tree/cloud-application/step/password-reset.md): Reset passwords and verify addresses by email with one mechanism: single-use, short-lived tokens stored only as a hash. - [Connect an identity provider](https://gradus.kazet.blog/tree/cloud-application/step/connect-identity-provider.md): Create the provider's tenant for each environment, register the callback URLs and keep its secrets out of the code. - [Verify provider tokens](https://gradus.kazet.blog/tree/cloud-application/step/verify-provider-tokens.md): Check the provider's tokens in your own service and create a local user at first sign-in, so the product never depends on the provider's ids. - [Your data is yours](https://gradus.kazet.blog/tree/cloud-application/step/data-ownership.md): Give the first journey's records an owner and scope every query to it, then prove one user cannot reach another's data. - [Google sign-in](https://gradus.kazet.blog/tree/cloud-application/step/google-sign-in.md): Let users sign in with Google over OIDC, linking an existing account only on a verified address. - [GitHub sign-in](https://gradus.kazet.blog/tree/cloud-application/step/github-sign-in.md): Let users sign in with GitHub, which is plain OAuth rather than OIDC: no ID token, and the address comes from a separate call. - [Microsoft sign-in](https://gradus.kazet.blog/tree/cloud-application/step/microsoft-sign-in.md): Let users sign in with Microsoft over OIDC, keyed on tenant and object id, because the email claim is not proof of an address. - [Apple sign-in](https://gradus.kazet.blog/tree/cloud-application/step/apple-sign-in.md): Let users sign in with Apple, whose client secret is a JWT you renew and whose name arrives only once. - [More providers](https://gradus.kazet.blog/tree/cloud-application/step/more-providers.md): Add the one provider this product's users really have, such as LinkedIn, GitLab or Discord, through the same identities record. - [Change email](https://gradus.kazet.blog/tree/cloud-application/step/change-email.md): Confirm a new address before it takes effect, tell the old one and keep it working until then. - [Sign-in methods](https://gradus.kazet.blog/tree/cloud-application/step/sign-in-methods.md): Show how an account can sign in and let users link and unlink methods, never removing the last one. - [Multi-factor authentication](https://gradus.kazet.blog/tree/cloud-application/step/multi-factor-auth.md): Add a second factor with recovery codes, set up and turned off in settings. - [Delete account](https://gradus.kazet.blog/tree/cloud-application/step/delete-account.md): Let users take their data and leave: an export, a deletion with a grace period and a clear list of what is kept. ## Testing & resilience: Tests, errors, SLOs - [Unit tests](https://gradus.kazet.blog/tree/cloud-application/step/unit-tests.md): Set up a test framework and write unit tests for the core logic, so regressions are caught before they ship. - [Centralized error handling](https://gradus.kazet.blog/tree/cloud-application/step/error-handling.md): Introduce typed errors and a centralized handler so failures are caught, logged and turned into consistent responses. - [Retries & circuit breakers](https://gradus.kazet.blog/tree/cloud-application/step/retries-and-circuit-breakers.md): Add retry-with-backoff and circuit breakers around calls to external services, so one failing dependency can't cascade into a full outage. - [Load & chaos testing](https://gradus.kazet.blog/tree/cloud-application/step/load-and-chaos-testing.md): Run load tests against realistic traffic patterns and chaos experiments that kill dependencies on purpose, to see what actually breaks. - [SLOs & error budgets](https://gradus.kazet.blog/tree/cloud-application/step/slos-and-error-budgets.md): Define service level objectives for latency and availability and track the error budget they leave you. - [Timeouts & bulkheads](https://gradus.kazet.blog/tree/cloud-application/step/timeouts-and-bulkheads.md): Give every outbound call a timeout and isolate resource pools so one slow dependency cannot exhaust everything. - [Test doubles & fixtures](https://gradus.kazet.blog/tree/cloud-application/step/test-doubles.md): Create fixtures, factories and fakes for external services, so tests run fast and without the network. - [Integration & end-to-end tests](https://gradus.kazet.blog/tree/cloud-application/step/integration-tests.md): Add integration tests against real dependencies in containers and a small end-to-end suite over the main user journeys. - [API schema & contract tests](https://gradus.kazet.blog/tree/cloud-application/step/api-contract-tests.md): Publish an API schema and test that the implementation honours it, so clients can trust the contract. ## Observability: Logs, metrics, traces, alerts - [Structured logging](https://gradus.kazet.blog/tree/cloud-application/step/structured-logging.md): Add structured, leveled logging with correlation IDs, so every request leaves a trail that can be followed. - [Metrics](https://gradus.kazet.blog/tree/cloud-application/step/metrics.md): Expose latency, error rate, traffic and saturation metrics (the four golden signals) for scraping by Prometheus or similar. - [Distributed tracing](https://gradus.kazet.blog/tree/cloud-application/step/distributed-tracing.md): Add distributed tracing with OpenTelemetry so a single request can be followed across every service it touches. - [Alerting](https://gradus.kazet.blog/tree/cloud-application/step/alerting.md): Define alert rules on the golden signals and route them to whoever is on call when a threshold is crossed. - [On-call & runbooks](https://gradus.kazet.blog/tree/cloud-application/step/on-call-and-runbooks.md): Set up an on-call rotation, write a runbook for each alert and hold a blameless review after every incident. - [Cost monitoring](https://gradus.kazet.blog/tree/cloud-application/step/cost-monitoring.md): Tag resources by service, track spend per environment and alert when a budget is about to be exceeded. - [Dashboards](https://gradus.kazet.blog/tree/cloud-application/step/dashboards.md): Build a dashboard per service showing the golden signals, with a top-level view of the whole system. - [Log aggregation](https://gradus.kazet.blog/tree/cloud-application/step/log-aggregation.md): Ship logs from every instance to a central store where they can be searched and correlated by request ID. - [Synthetic monitoring](https://gradus.kazet.blog/tree/cloud-application/step/synthetic-monitoring.md): Run external uptime checks, expiry warnings and synthetic journeys from outside the system, so you learn of outages before customers do. - [Browser error reporting](https://gradus.kazet.blog/tree/cloud-application/step/browser-errors.md): Catch the errors that happen in your users' browsers, with readable stack traces and no personal data. ## Data & storage: DB, cache, queues - [Choose a database](https://gradus.kazet.blog/tree/cloud-application/step/choose-database.md): Recommend a database for this product: options that fit the first journey and its likely scale, one recommendation, one decision record. - [Managed Postgres](https://gradus.kazet.blog/tree/cloud-application/step/db-postgres-provision.md): Provision a managed Postgres instance in the infrastructure code and connect the service to it through a pooled connection and a secret, with a local instance in a container for development. - [Postgres migrations](https://gradus.kazet.blog/tree/cloud-application/step/db-postgres-migrations.md): Add a migration tool, write the first schema from the first journey and run migrations on every deploy before the new revision takes traffic. - [Document database](https://gradus.kazet.blog/tree/cloud-application/step/db-document-provision.md): Provision a managed document database such as Firestore or DynamoDB in the infrastructure code and connect the service to it with the cloud's own identity, with an emulator for development. - [Document model & indexes](https://gradus.kazet.blog/tree/cloud-application/step/db-document-model.md): Model the first journey as collections and documents, decide what is embedded and what is referenced and declare the indexes the journey's queries need. - [Data access layer & seed data](https://gradus.kazet.blog/tree/cloud-application/step/data-access-layer.md): Put every query behind one data access layer, seed the database with the data the first journey needs and persist the journey's first write. - [Users & identities](https://gradus.kazet.blog/tree/cloud-application/step/users-and-identities.md): Store a person once: a users record with its own stable id and a separate identities record, so a password, Google and GitHub can all attach to one user. - [Caching layer](https://gradus.kazet.blog/tree/cloud-application/step/caching.md): Add a cache such as Redis in front of the hottest, most expensive reads. - [Object storage](https://gradus.kazet.blog/tree/cloud-application/step/object-storage.md): Add object storage for files, uploads and backups, so large blobs stop living in the database or the filesystem. - [Message queue](https://gradus.kazet.blog/tree/cloud-application/step/message-queue.md): Add a message queue or event stream so services can communicate asynchronously instead of calling one another directly. - [Stream processing](https://gradus.kazet.blog/tree/cloud-application/step/stream-processing.md): Process events as they arrive with a stream processor, keeping derived views up to date without batch jobs. - [Backups & recovery](https://gradus.kazet.blog/tree/cloud-application/step/backups-and-recovery.md): Automate database backups with point-in-time recovery and rehearse a restore until it is boring. - [Replicas & failover](https://gradus.kazet.blog/tree/cloud-application/step/replicas-and-failover.md): Add a read replica for heavy reads and automatic failover so the loss of one database node is not an outage. - [Indexing & query tuning](https://gradus.kazet.blog/tree/cloud-application/step/indexing-and-query-tuning.md): Find the slow queries, add the indexes they need and keep a query plan check in the test suite. - [Full-text search](https://gradus.kazet.blog/tree/cloud-application/step/full-text-search.md): Add a search index for free-text lookups so users find things by meaning, not by exact match. ## Serving & compute: HTTP, workers, scaling - [Routing & middleware](https://gradus.kazet.blog/tree/cloud-application/step/routing-and-middleware.md): Add structured routing and shared middleware for CORS, body parsing and request logging. - [Background workers](https://gradus.kazet.blog/tree/cloud-application/step/background-workers.md): Add a background job/worker queue so slow or non-urgent work runs asynchronously instead of blocking a request. - [Container orchestration](https://gradus.kazet.blog/tree/cloud-application/step/container-orchestration.md): Write Kubernetes manifests or a Helm chart so the application can run as multiple coordinated instances. - [Serverless functions](https://gradus.kazet.blog/tree/cloud-application/step/serverless-functions.md): Extract a suitable piece of the workload into a serverless function that scales to zero between invocations. - [Autoscaling](https://gradus.kazet.blog/tree/cloud-application/step/autoscaling.md): Scale instances on real signals such as queue depth or request latency, with sane floors and ceilings. - [Scheduled jobs](https://gradus.kazet.blog/tree/cloud-application/step/scheduled-jobs.md): Run recurring work on a schedule with locking, so the same job never runs twice at once. - [API gateway & versioning](https://gradus.kazet.blog/tree/cloud-application/step/api-gateway.md): Put an API gateway in front of services to handle versioning, TLS termination and routing by path. - [API documentation](https://gradus.kazet.blog/tree/cloud-application/step/api-documentation.md): Generate interactive API docs from the schema and publish them with each release. - [Transactional email](https://gradus.kazet.blog/tree/cloud-application/step/transactional-email.md): Send mail from the product's own domain through one provider, with SPF, DKIM and DMARC in place and a local catcher so tests never reach a real inbox. - [Email deliverability](https://gradus.kazet.blog/tree/cloud-application/step/email-deliverability.md): Hear about mail that fails: record bounces and complaints, stop sending to those addresses and retry only what can recover. ## Frontend: Second app, UI, browser tests - [Where the frontend lives](https://gradus.kazet.blog/tree/cloud-application/step/choose-repo-layout.md): Decide whether the frontend is a workspace in this repository or a repository of its own, and write it down. - [A workspace in the same repository](https://gradus.kazet.blog/tree/cloud-application/step/frontend-workspace.md): Turn the repository into a workspace with the service and the frontend as packages that share tooling, one lockfile and one CI. - [A second repository](https://gradus.kazet.blog/tree/cloud-application/step/frontend-repository.md): Create a second repository for the frontend with its own README, CI and knowledge base, and a link each way between the two. - [Knowledge across repositories](https://gradus.kazet.blog/tree/cloud-application/step/knowledge-across-repos.md): Register both knowledge bases in one place the assistant reads in every session, so a change in the API is visible from the frontend repository and the other way round. - [Choose a frontend approach](https://gradus.kazet.blog/tree/cloud-application/step/choose-frontend.md): Recommend how the frontend is built: a browser framework or server-rendered HTML, judged against the first journey and your experience, written as a decision record. - [React with Vite](https://gradus.kazet.blog/tree/cloud-application/step/frontend-react.md): Scaffold a React application with Vite in the frontend's home and show one page that calls the service's health route. - [Server-rendered HTML with HTMX](https://gradus.kazet.blog/tree/cloud-application/step/frontend-htmx.md): Serve HTML from the service itself with a template engine and HTMX for partial updates, with one page that shows the health route's answer. - [The first journey in the browser](https://gradus.kazet.blog/tree/cloud-application/step/journey-ui.md): Build the first journey end to end through the UI: every screen from docs/product/mvp.md, talking to the service, persisting through the data access layer. - [Frontend checks in CI](https://gradus.kazet.blog/tree/cloud-application/step/frontend-ci.md): Lint, type check, unit tests, a size budget and a production build of the frontend on every push, with development-only code kept out of what ships. - [Design tokens & components](https://gradus.kazet.blog/tree/cloud-application/step/design-tokens.md): Put colours, type and spacing into tokens with a light and a dark theme, and build the few components the journey needs from them. - [Accessibility pass](https://gradus.kazet.blog/tree/cloud-application/step/accessibility-pass.md): Keyboard, contrast, names and a screen reader through the whole journey, with automated checks in CI to keep it that way. - [Deploy the frontend](https://gradus.kazet.blog/tree/cloud-application/step/frontend-deploy.md): Serve the built frontend behind a CDN from CI on every merge to main, on the same origin as the API or with CORS configured once and properly. - [Browser end-to-end tests](https://gradus.kazet.blog/tree/cloud-application/step/browser-e2e-tests.md): A small browser suite over the first journey against the deployed frontend, in three engines, at phone and desktop widths and in both colour schemes. - [Real 404s & redirects](https://gradus.kazet.blog/tree/cloud-application/step/not-found-and-redirects.md): Answer an unknown address with a real 404, never the application with a 200, and move old addresses with a 301. - [Cache & compression](https://gradus.kazet.blog/tree/cloud-application/step/cache-headers.md): Let browsers keep hashed assets for a year and always check the HTML, and compress what is sent. - [Custom domain](https://gradus.kazet.blog/tree/cloud-application/step/custom-domain.md): Attach the product's own domain to the frontend and the service over HTTPS, renewed, locked and safe from spoofing, so every link you share from now on stays yours. - [Landing page](https://gradus.kazet.blog/tree/cloud-application/step/landing-page.md): Turn the pitch into the page a stranger lands on: who it is for, what it does and one clear way to start. - [Share previews](https://gradus.kazet.blog/tree/cloud-application/step/share-previews.md): Give every public page a title, a description and a 1200 by 630 image in the HTML the server sends, because link crawlers do not run JavaScript, and check that each crawler really gets them. - [Privacy & terms](https://gradus.kazet.blog/tree/cloud-application/step/privacy-and-terms.md): Say what the product collects, why and for how long, on public pages linked from everywhere. - [Icons & manifest](https://gradus.kazet.blog/tree/cloud-application/step/icons-and-manifest.md): Make one mark and produce the small icon set from it: favicon, touch icon and web manifest, so the product looks like itself in a tab and on a home screen. - [Crawl rules & sitemap](https://gradus.kazet.blog/tree/cloud-application/step/crawl-rules.md): Decide who may crawl the product, say so in robots.txt, list the public pages in a sitemap and an llms.txt and keep every other environment out of search. - [Web performance](https://gradus.kazet.blog/tree/cloud-application/step/web-performance.md): Measure the key pages on a mid-range phone, fix what makes them slow and set a budget so they stay fast. - [Search engines](https://gradus.kazet.blog/tree/cloud-application/step/search-engines.md): Describe the product to search engines with structured data, prove you own the site in their consoles and send them the sitemap. - [Sign-in flow](https://gradus.kazet.blog/tree/cloud-application/step/sign-in-flow.md): The login, sign-up and sign-out screens, protected pages and a return to where the user was going, on whichever route you took. - [Settings page](https://gradus.kazet.blog/tree/cloud-application/step/settings-page.md): A signed-in page with a section per concern, the home of everything a user can change about their account. - [Profile](https://gradus.kazet.blog/tree/cloud-application/step/profile-page.md): Let users change their display name and claim a unique username, with reserved words and letter case handled once. - [A feature end to end](https://gradus.kazet.blog/tree/cloud-application/step/feature-end-to-end.md): Build the spec as one vertical slice: data, API, screens, tests and events, shipped dark behind a flag, then on for you, then for everyone.