Construir, medir, documentar, repetir
When the first version of this article was published, the product was still called PuzzleHub. It now lives inside Blupoli as Blupoli Puzzles. The name changed, several architectural details changed, and the public product has matured—but the question behind the article still matters: how do you build a platform that can host genuinely different puzzles without turning into a folder full of unrelated pages?
The difficulty is that “build a game” and “build a game platform” are different jobs. A single game can focus on its mechanic: rules, state, interaction and completion. A platform has to solve everything that repeats around those mechanics: navigation, catalogue, difficulty, help, statistics, persistence, themes, accessibility, localization, responsive behavior, editorial content and a safe way to ship changes. The more diverse the catalogue becomes, the more important it is to keep those responsibilities separate.
Over time we started thinking about Blupoli Puzzles as three systems laid on top of one another. The first is the games themselves: engines, generators, solvers and puzzle-specific representations. The second is the platform: the shared experience that makes all of those games feel like one product. The third is the process: data, Git, tests, CI, documentation and tools that let the first two systems evolve without relying on memory or an impossible amount of manual checking.
The first layer: every puzzle deserves a model that understands its logic
A large platform naturally tempts you toward a universal abstraction. It would be convenient to have one “puzzle engine” that receives a declarative rule set and solves everything. In practice, puzzle mechanics speak different mathematical languages. Sudoku revolves around uniqueness constraints across rows, columns and boxes. Slant combines local clues with a global no-cycle condition. Aquarium reasons about water levels inside regions. Dominosa can be modeled as an exact-cover problem. Logic Grid deals with relationships between entities. Competitive games add turns, evaluation and an opponent.
Trying to hide all of that behind one implementation can simply move complexity instead of removing it. We prefer to share contracts before sharing algorithms. Each engine is free to use the representation that best fits its mechanic as long as the platform can start a session, apply or receive actions, recognize completion, persist the relevant state and expose useful metadata.
That philosophy is explored in more depth in our article on puzzle-engine architecture. The compact version is this: specialization is healthy inside the engine; consistency belongs outside it. That lets us optimize each puzzle honestly without giving up a coherent product.
An engine is not finished when it accepts moves
The first implementation of a puzzle can feel complete as soon as the board responds and a win condition exists. On a platform, that point is closer to the beginning. A published session has to be generated or loaded reliably, obey its rules, remain reproducible when that matters and offer a difficulty label that means something. In many puzzle families we also need evidence that the challenge has exactly one solution.
That introduces two pieces that initially look like internal developer tools: generators and solvers. The generator proposes an instance. The solver checks properties. When those responsibilities are blurred, it becomes harder to know what we have actually proven. Keeping them separate creates a valuable boundary: producing a candidate is not the same as showing that the candidate deserves to be published.
We cover this distinction in “Generating a puzzle is not the same as solving one”. From a platform perspective, the consequence is that logical quality becomes part of publication. We do not want a player to discover after ten minutes that the board was ambiguous, trivial or impossible.
Difficulty should not be a disguised board-size selector
One common shortcut is to equate size with difficulty: small is easy, large is hard. Sometimes size matters, but it is not a definition. A large board can repeat obvious deductions. A compact one can require a precise chain of reasoning. Meaningful difficulty should reflect the kind of deduction required, the amount of branching, how much information is exposed or another metric suited to the mechanic.
This is another reason specialized engines are useful. There is no universal “logic difficulty” score that means the same thing across every puzzle. The platform can still present a human-friendly scale while each engine translates that scale into parameters and measurements appropriate to its domain.
The separation keeps the interface from learning puzzle theory. The shell asks for a difficulty level. The engine decides what that promise means and how to create or select an instance that reasonably fulfills it.
The second layer: a platform prevents every engine from becoming its own app
If every engine had to implement navigation, buttons, help, themes, statistics, persistence and responsive layout, the project would grow by copying. At first the differences would be minor. After enough iterations, each page would have its own definition of “new game,” its own spacing, its own error treatment and its own set of shortcuts.
The platform exists to remove that repeated work from the engines. The game shell provides hierarchy, a place for the board, configuration and action areas, global states and a connection to the rest of the product. It does not make all boards look the same. It removes responsibilities that are not specific to the mechanic.
This model grew substantially from our Sudoku work. In the article about Sudoku as a UI laboratory we explain how one familiar mechanic helped us separate local decisions from general principles. The follow-up on designing a shared UI for very different puzzles describes how those principles become reusable product infrastructure.
The useful kind of consistency lives around the board
A puzzle platform needs consistency without erasing character. The safest place to share decisions is usually around the reasoning surface: navigation, page structure, help, product states, accessibility, themes and recurring tools. Inside the board, the mechanic should remain in charge.
This avoids two equally bad extremes. If everything is custom, switching games means relearning the application. If everything is uniform, the board loses the signals it needs. Nonogram should still look and behave like Nonogram. Slitherlink needs edges and clues. Logic Grid needs relational tables. A CPU game needs turns. What they can share is the expectation of where to start a new session, where help lives and how global actions behave.
Over time this became one of our most useful product principles: learn Blupoli once, then learn only what makes the next puzzle special.
Persistence before accounts: create value without waiting for the entire backend
Platforms can fall into another trap: postpone useful features until a complete account, backend and synchronization system exists. For many puzzle experiences, local persistence already creates real value—continue a session, remember preferences, retain progress and record basic statistics.
Building those concepts locally also forces us to design them before synchronizing them. What identifies a session? Which fields belong to the engine and which are product metadata? What statistics make sense for a puzzle, and which belong to competitive games? What happens when a board format changes? Answering these questions first reduces the risk of freezing a poor model into a server API.
If accounts later synchronize state across devices, that model will already have a history of real use. Infrastructure arrives to support a known need instead of inventing one.
Statistics should respect the mechanic they describe
“Games played” and “games won” are an easy common denominator, but they are not enough. A puzzle may care about solves, time, moves, hints or variants. A game against the CPU introduces wins, losses, draws and opponent difficulty. Flattening those into one schema because it is convenient to store produces numbers that are easy to collect and hard to interpret.
We therefore want to share the statistics framework without pretending that every game produces identical meaning. The engine exposes context; the platform records and presents it. The same architectural idea keeps returning: common contract, specialized semantics.
This is a useful signal that architecture is more than technology choice. It is a way of assigning responsibility. When one layer knows too many details from another, growth starts producing exceptions.
The catalogue becomes a product when it no longer fits in your head
With a handful of games, a list of links works. As variety grows, discovery becomes a user problem of its own. Someone may search for a known title, while another person wants “something deductive” without knowing any names. Categories, filters, search and descriptive context stop being decoration and begin guiding decisions.
That changes the importance of metadata. Category, family, difficulty, availability, controls and descriptions need real sources of truth. The catalogue should not manually reconstruct information that already belongs to game data. Editorial pages and category surfaces should not copy current counts that become stale after the next release.
The broader product audit—described in “When to stop adding features and audit the product you already have”—made it even clearer that the catalogue could no longer behave like a simple list.
Internationalization is an architecture test disguised as translation
Languages punish duplication. If category names live in several files, if an engine directly emits Spanish sentences, or if navigation contains hardcoded routes, adding another locale multiplies inconsistency. Internationalization forces a product to distinguish identity from presentation.
A puzzle remains the same entity when the language changes. Its rules and state should not be duplicated; names, descriptions, UI and editorial content are localized. Generated natural-language clues need semantic representation before they become sentences.
This pressure exposed weak boundaries and eventually pushed our editorial and routing systems toward more explicit sources of truth. Internationalization was not just translation work. It was a test of whether the product really knew what belonged to data and what belonged to presentation.
Accessibility scales better when the common layer carries the baseline
Auditing accessibility game by game at the end is expensive and unreliable. Some behaviors can be inherited: visible focus, semantic buttons, disabled states, document structure, baseline contrast and common navigation. The more of that lives in shared components, the less every engine depends on remembering the same checklist.
The mechanic still needs specific work. Complex grids may require tailored labels. Canvas-based surfaces may need alternatives. A gesture may need a keyboard equivalent. The platform cannot solve every case, but it can raise the floor and make exceptions explicit.
This is architecture as leverage again. One base-layer improvement can benefit many games. A shared system earns its complexity when quality can propagate through it.
The third layer: process prevents speed from turning into loss of control
The faster a product can change, the more important verification becomes. That is especially true when AI agents participate in research, implementation or review. The ability to produce changes increases. So does the need for context, bounded tasks and independent checks.
Git gives us history and units of change. Documentation preserves decisions the diff cannot explain. Tasks translate broad goals into reviewable work. CI runs repeatable contracts. Tests protect rules. Builds validate routes, metadata, assets and other invariants. No single mechanism guarantees quality, but together they reduce dependence on human memory.
In our article about AI as part of the development team we explore how this changes the relationship between speed and review. The principle that matters here is simple: automating production without automating verification mostly automates uncertainty.
Git is technical memory, but product memory needs context
A repository knows exactly what changed. It does not always preserve why that change mattered. A commit can tell us that a validator was added, a route changed or a generator was fixed. Months later, the product reason may be hard to reconstruct. That is why code, documentation and editorial storytelling have to connect.
Not every implementation detail needs a public article. The process simply needs enough context for a decision to be reviewed and for a future change to avoid repeating an old failure because nobody knew why the current shape existed. This discipline also helps agents: the more explicit the contract, the less intent has to be inferred from scattered files.
Project memory becomes a quality tool. It does not stop us changing our minds. It makes us aware when and why we do.
CI should protect product properties, not merely syntax
A pipeline that only confirms JavaScript parses leaves many real risks untouched. In Blupoli Puzzles the build can verify routes, localization, metadata, catalogue state, assets and other contracts. Engines can run self-tests. Generators can exercise public presets. Quality gates can detect missing content or broken references.
The closer a check is to the property we actually care about, the less likely a regression can hide. If a puzzle requires unique solutions, verify uniqueness. If an editorial page needs metadata and language alternates, validate them. If an unfinished game must not appear as playable, encode that state in data and derive the public surfaces from it.
Automation does not replace playing or reading. It lets human review spend less time rechecking invariants a machine can prove more reliably.
AI agents increase the value of well-defined tasks
An instruction such as “improve the website” has too many valid interpretations. A task that describes the problem, scope, constraints, precedent and definition of done creates work that can actually be reviewed. This was always good engineering practice; agents make it central.
Autonomy needs boundaries. An agent can research a mechanic, implement a component, review tests or prepare content, but each change needs a clear edge and a way to verify the result. Speed comes from parallelizing well-specified work, not from removing review.
The discipline helps human development too. Clear tasks reduce giant changes, make pull requests understandable and create an objective point at which work can be called complete.
The simplicity of a static web product remains an advantage when used deliberately
A large portion of Blupoli Puzzles can be served as generated HTML, CSS, JavaScript and assets. That keeps infrastructure modest and makes many surfaces fast to distribute. It does not mean everything must remain static forever. It means a service should appear when it protects a responsibility the client cannot handle correctly.
Accounts, cross-device synchronization, authoritative competitive rankings or other trust-sensitive features may require backend services. The rule is to introduce that boundary because a need exists, not because a “serious” platform is expected to accumulate servers.
This preference for the smallest useful layer also shaped our Android path. As explained in the Capacitor article, we prefer to reuse the web product and add native capabilities where they provide real value before duplicating the whole application.
Good architecture lets local problems stay local
A shared platform can fail in the opposite direction by turning every special need into global infrastructure. That creates complexity too. If one puzzle needs a unique tool, it may belong in that engine. If several families repeat the pattern, it may deserve an official extension point.
The key is recurrence. We do not abstract because a future case is imaginable; we abstract when real cases provide evidence. This keeps the shared core smaller and prevents the game shell from becoming a giant collection of switches nobody understands.
Accepting specialization is what allows Blupoli Puzzles to grow without flattening its variety. A platform is not a mold. It is infrastructure that makes genuinely repeated work cheaper.
Devlog also works as a tool for thinking
Writing about the process forces implicit decisions to become sentences. Explaining why engines are separated, why we paused expansion to audit, or why one mobile path was chosen makes it easier to notice whether the reasoning still holds.
Public writing should not be a changelog with better typography. It becomes valuable when technical work is turned into an idea that is useful to someone reading from outside the repository. For us it also creates a record of changing definitions—what “finished” meant, which priorities shifted and which assumptions were replaced.
As Blupoli’s editorial architecture evolved, some of these stories belong in the Blog and others in the Devlog depending on their intent. The distinction improves clarity, but the original goal remains: explain the product without pretending decisions appear from nowhere.
The audit was the signal that the platform had entered another phase
At some point, adding one more puzzle stopped being automatically the best way to move forward. The catalogue was diverse enough to reveal system problems. UI needed consolidation. Public availability had to become more honest. The build needed stronger invariants. Taxonomy and discovery mattered nearly as much as coverage.
Pausing to audit did not reduce the ambition of the product. It made that ambition more sustainable. The first stage proved that we could build engines. The next needed to prove that we could maintain them, explain them, verify their quality and ensure every new addition inherited more capability than the last.
That phase change is one reason the three-layer model remains more useful than thinking only in terms of a catalogue.
The product improves when each layer can evolve at its own pace
Engines should not have to change when the header changes. The catalogue should not depend on how a solver stores internal states. CI can become stricter without redesigning the boards. This independence reduces the blast radius of changes and makes experimentation safer.
At the same time, the layers need contracts. Engines must expose enough information for the shell. The platform must produce data the build can validate. The process must know invariants without reimplementing every puzzle rule. Designing those contracts is a major part of architecture.
When a boundary works, improvements travel in the right direction. A common fix helps many games; a mechanic-specific innovation does not force unrelated engines to change; a new quality gate catches problems without coupling itself to irrelevant detail.
We do not want the next game to start from zero
The most interesting measure of a platform is not how many pages it contains but how much repeated work it eliminates. If a new engine receives navigation, themes, persistence, help, shared states, localization and quality contracts by default, development effort can concentrate on the mechanic. Earlier games have left infrastructure behind.
That compounding effect is the goal. The first game is expensive because it also creates part of the platform. The second tests the separation. The tenth reveals recurring patterns. The next one should start from a higher floor, not from a larger copy.
This idea connects directly to the origin story of the product: Blupoli Puzzles began by asking what had to change so that the second puzzle would not require rebuilding the first.
Three layers, one simple experience for the player
All of this can sound architectural, but the player should never have to think in three systems. They should open a page, choose a puzzle, understand what to do, enjoy the challenge and trust that the product behaves coherently. Engines, shell and pipeline exist so that the visible experience can remain simple.
Architecture is doing its job when it absorbs complexity instead of exposing it. Process is doing its job when problems are caught before users meet them. The platform is doing its job when familiar behavior fades into the background and the puzzle gets the attention.
We still build Blupoli with that idea. Names, tools and scripts will continue to change. Some decisions from 2026 will be replaced. But the mental model remains useful: specialize logic where specialization matters, share the experience where sharing helps, and verify change through a process that does not require anybody to remember everything. That is what turns a collection of games into a platform capable of learning as it grows.