Productivity.
Use conventions, generators, and one coherent application structure to remove routine decisions. The goal is to move from an idea to useful software without spending the first week assembling a framework.
A framework with a point of view
Amber is deliberately opinionated about both the experience of building software and the shape of the code. The values tell us how to work together. The coding beliefs make those values concrete in every application.
Shared values
Productivity, performance, and happiness describe what Amber should make possible. Humility, respect, and trust describe the community required to sustain it.
Use conventions, generators, and one coherent application structure to remove routine decisions. The goal is to move from an idea to useful software without spending the first week assembling a framework.
Build on Crystal's type system, native compilation, concurrency, and macros. Performance should be part of the framework's architecture—not a tax the application team pays after the product succeeds.
Make everyday web development simple, comfortable, and understandable. Clear errors, readable defaults, useful diagnostics, and fast feedback protect the attention that developers need for their actual product.
Amber is not the center of the universe. Borrow ideas that have been tested elsewhere, acknowledge incomplete work, invite correction, and stay open to better answers from Crystal and the wider web community.
Treat contributors and users as people whose time matters. Document the path, preserve stable references, explain breaking changes, and design defaults that do not surprise the next person maintaining the application.
Build code and processes that another developer—or coding agent—can understand and safely extend. Share context, keep responsibilities explicit, and let capable collaborators drive when they are closest to the problem.
Coding beliefs
An Amber codebase should reveal its intent without archaeology. A route names the request path, a controller coordinates the work, respond_with declares the available representations, ECR owns HTML, and local browser assets finish the experience.
Belief 01
Controller boundary
HTML and JSON are two representations of the same resource, not two excuses to repeat the application logic. Load the data once. Use a Rails-inspired respond_with block to state exactly what the action can return. Amber selects a representation from the request's Accept header or extension and returns 406 Not Acceptable when none matches.
class ArticlesController < ApplicationController
def show
article = ArticleCatalog.fetch(params["slug"])
respond_with do
html { render("show.ecr") }
json { article.to_json }
end
end
end
GET /articles/amber-way
Accept: text/html
ECR + layout
GET /articles/amber-way
Accept: application/json
JSON
Belief 02
Application structure
Amber CLI generates one predictable home for each responsibility. This is the supported V2 web shape—not a suggestion that every application invents again.
my_app/
├── config/
│ ├── application.cr
│ ├── routes.cr
│ └── environments/
├── public/
│ ├── css/app.css
│ └── js/app.js
├── spec/
│ └── controllers/
├── src/
│ ├── my_app.cr
│ ├── controllers/
│ │ ├── application_controller.cr
│ │ └── home_controller.cr
│ └── views/
│ ├── home/index.ecr
│ └── layouts/application.ecr
└── shard.yml
config/routes.crMaps an HTTP method and path to a controller action through an explicit pipeline.
src/controllers/Loads resources, applies request policy, and declares the available response formats.
src/views/Owns the server-rendered HTML, reusable partials, and the shared document layout.
public/Serves local CSS, JavaScript modules, fonts, and images without a front-end build step.
spec/Verifies the request contract at the same boundary a browser or API client uses.
Jobs, mailers, channels, models, and schemas follow the same rule: focused directories with stable generator destinations. See the complete generated web template.
Belief 03
HTML boundary
ECR keeps HTML visible, lets Crystal locals cross the rendering boundary, and makes layout composition explicit. Dynamic values are escaped; only framework-rendered template content is inserted as trusted markup.
<article class="article-shell">
<p class="eyebrow">Field note</p>
<h1><%= escape_html(article[:title]) %></h1>
<p><%= escape_html(article[:summary]) %></p>
<%= render(
partial: "articles/_meta.ecr"
) %>
</article>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<%= content %>
<script type="importmap">
{"imports":{"app":"/js/app.js"}}
</script>
<script type="module">import "app";</script>
</body>
</html>
render("show.ecr")
Partialrender(partial: "articles/_meta.ecr")
No layoutrender("card.ecr", layout: false)
Escape valuesescape_html(value)
Belief 04
Front-end baseline
A complete front end does not require an external library. Start with semantic HTML from ECR, locally served CSS, standard JavaScript modules, and a browser-native import map. Add dependencies only when the application has earned the extra release and security surface.
Routes work without JavaScript, metadata stays in the layout, and progressive enhancement begins from a real document.
Custom properties, Grid, Flexbox, container-aware responsive rules, and reduced-motion fallbacks cover the common system.
The import map points a bare name such as app at a local module. No npm install, bundle, or CDN is required.
This website is the proof
respond_withBelief 05
Measured performance
The July 17, 2026 Amber V2 performance lab measured a mixed, 1,000-route application—not a static-response best case. The current JSON path sustained a median 21,795 whole HTTP requests per second on the smallest one-vCPU target.
Current Amber JSON params · HTTP/1.1 keep-alive
What this does not claim: it is not a static-endpoint estimate, a cross-framework ranking, a production capacity promise, or an SLA for every Amber application. It is a dated result for a documented workload. The full seven-variant matrix delivered 18,728,053 successful responses; application code, hardware, proxies, databases, and traffic shape will change your result.
Opinionated by design
Amber should provide a coherent first-party answer wherever the framework can responsibly do so. Every external dependency adds another project, release process, and delivery chain an application must trust. Bringing proven responsibilities into the framework reduces that exposure and makes the supported path easier to test as one system.
Fewer dependencies reduce supply-chain and disruption risk; they do not eliminate it. First-party code still requires review, maintenance, release discipline, and clear security boundaries.
Choose a complete convention for the common path, document it end to end, and make extension a deliberate decision instead of setup homework.
Prefer first-party capabilities when Amber can own their quality and compatibility. Add third-party packages when they provide clear value that the framework should not duplicate.
Personify major architectural boundaries as crew members; keep focused products and systems—such as Asset Pipeline—named for the work they perform.