Developing a large feature from scratch often starts deceptively easy. The client explains the idea, the team discusses the screens, developers roughly understand the architecture. It seems ready to start. But as soon as you move from the general concept to specific behavior, questions arise, each with its own answer from every team member.

Let's take a web system for booking shared equipment. An employee selects a projector and a time, confirms the booking, and cancels it if necessary. It sounds like a few forms and CRUD operations. The illusion of simplicity disappears when two people book the same projector at the same time, the temporary reservation gets stuck forever, and the canceled request continues to occupy time in the calendar.

Let's examine how to go from such an idea to implementation through LLM: first, make the rules visible, then agree on the model and contracts, and then pass small tasks to the agent.

Between endless meetings and premature code

The first way to deal with uncertainty is to talk more. In a meeting, everything seems clear: "we reserve for some time," "we prevent double booking," "after cancellation, we free up." But in the next discussion, it turns out that one participant understood "reservation" as ten minutes, another as the entire selected interval, and the third thought that availability is checked only at confirmation.

The team gathers again, clarifies the terms, and reaches an agreement. This can take hours and days: conversations create the feeling of progress. The problem arises when the result doesn't become a description that can be checked entirely.

Another extreme is to write code faster so that there's something to discuss. There's also clear logic here: a working screen is more specific than a conversation. However, assumptions are already becoming tables, processors, client models, and tests. When it turns out that the temporary reservation must block the equipment immediately, several related parts need to be changed. The more implementations are built on an incorrect assumption, the more expensive the correction and the broader the area of possible errors.

Качественная шкала: от простых, но неоднозначных разговоров к точным изменениям в коде; модели, диаграммы и контракты выделены в середине
Quality spectrum: from simple but ambiguous conversations to precise code changes; models, diagrams, and contracts are highlighted in the middle

*Figure 1. The accuracy of the description increases along with the effort required to prepare and change it.*

Code most precisely defines executable instructions. But it's possible to implement incorrect behavior.

Between conversation and implementation there's a convenient area: data models, behavior diagrams, and interface contracts. They already force you to answer uncomfortable questions, but changing state transitions or entity relationships is usually easier than redoing the functionality they affect. In this article, we will refer to such an approach as Spec driven development: implementation relies on a coordinated description of the solution.

Route from idea to implementation

Процесс разработки: обсуждения, заметки, требования, ERD, Use Case, C4 Context и Container, State Machine, Sequence, Activity, OpenAPI, Markdown-спецификация, BDD, код и тесты
Development process: discussions, notes, requirements, ERD, Use Case, C4 Context and Container, State Machine, Sequence, Activity, OpenAPI, Markdown specification, BDD, code, and tests

Figure 2. The main project attention falls in the middle of the route. LLM helps at all stages; the person checks the meaning and makes decisions.

For example, let's agree that the reservation lasts ten minutes, after which it cannot be confirmed. A confirmed reservation and an active reservation occupy the selected interval. Cancellation frees it. Payment, issuance, and return of equipment are outside the scope of the feature.

The diagram defines the order of clarification, not a prohibition to go back. A conflict found in the Sequence may require changing the requirement. Then we update the related artifacts to the implementation of the dependent step. Also, there is no need to describe the entire future product: first, we go through the route for one completed scenario.

1. Meetings and discussions: figuring out what we are building

We start with the people who need the feature. What problem is currently occurring? Who books the equipment? What should the user get after confirmation? What situations are considered unacceptable? Specific user stories are helpful: "two employees came for one projector," "a person opened a form and left, the equipment remained reserved."

In our example, we need to figure out the lock time, reservation period, meaning of cancellation, and behavior during competitive requests. We separately clarify boundaries: we book a specific projector with an inventory number, not an arbitrary unit from a category.

LLM can pre-form questions, group conflicting wishes, and suggest boundary situations. However, the answer "the user probably expects ten minutes" remains a model's assumption. The choice of duration belongs to the people responsible for the process.

A useful result of the meeting is agreed solutions and a short list of questions with responsible parties. If a question affects data or the public interface, it needs to be resolved before designing the corresponding part. Other, independent parts of work can continue.

2. Notes and summaries: keeping the agreed decision

After the meeting, a compact summary is needed. Full disclosure preserves the conversation, but forces the next reader to re-extract decisions from it. The note should show the chosen option, its reason, and the remaining uncertainty.

For example:

Decision: reserve blocks equipment for ten minutes from the moment of creation. Reason: the user must have time to check the parameters before confirming. The expiration is determined by the server. Question: can the reserve be extended? Decision for the first version: extension is not supported.

Such text already distinguishes an agreed-upon time from a randomly mentioned idea.

It is convenient to assign LLM to draft the outline and a list of contradictions between meetings. Participants check that the model did not turn a question into a decision and did not lose an exception. At this stage, it is especially easy to get a neat, persuasive text that falsely attributes a non-existent agreement to the team.

3. Formal requirements: replace "clear" with verifiable rules

Now we formulate the rules so that two executors get the same result. Instead of "we do not allow double booking," we write what requests occupy the resource and what to consider as an intersection.

| Rule | Consistent Behavior | | — | — | | BR-01 | An interval has the form [startsAt, endsAt): start is inclusive, end is exclusive; start is earlier than end. | | BR-02 | When created, HELD is set, and the reservation duration is ten minutes according to server time. | | BR-03 | Equipment is blocked by CONFIRMED and HELD where now < holdExpiresAt. | | BR-04 | Checking for intersection and creating a reservation constitute a single atomic operation. | | BR-05 | When now >= holdExpiresAt, the reservation has expired; it cannot be confirmed. | | BR-06 | Confirmation and cancellation are only available to the booking owner. |

Identifiers help link solutions to diagrams and scenarios. An agent can be tasked with finding a rule without an example or an ambiguous word. The final meaning is affirmed by the person. Detailed transitions and handling of repeated actions will be fixed later when we consider the booking lifecycle.

A good requirement withstands the question "How will we know it's been completed?"." The requirement "The system correctly handles simultaneous requests" does not provide such an answer. "Exactly one of two overlapping requests for free equipment is successfully created" already defines an observable check.

4. Data model and ERD: determining what exists in the system

The model contains only three entities: user, equipment unit, and reservation. Each reservation belongs to one user and relates to one unit. A user and equipment can have multiple reservations.

ERD: User и Equipment связаны отношениями один ко многим с Reservation; у бронирования отдельно указаны интервал использования, статус и срок резерва
ERD: User and Equipment are linked with one-to-many relationships to Reservation; the reservation has separately specified usage interval, status, and reservation period.

Figure 3. startsAt and endsAt describe equipment usage, holdExpiresAt — the time when the user makes a decision.

ERD shows the structure and cardinality of relationships. The rule prohibiting intersections needs to be written nearby: a regular foreign key is insufficient for it.

LLM can prepare a textual schema and verify field names against requirements.

It's useful to immediately separate stored data from the public response. The server needs the record owner, but there's no need to return the user's external identity to the client for confirming their reservation. On the contrary, the interface needs to know the reservation duration to explain the remaining time. Such analysis prevents the common practice of copying the entire table in the API and helps formulate a future response schema without being tied to a specific ORM.

5. Use Case: Describing the user's goals

Use Case helps verify that the project addresses the user's task. For an employee, we identify four goals: check availability, create a temporary reservation, confirm, and cancel the booking.

UML Use Case: сотрудник связан с просмотром доступности, созданием резерва, подтверждением и отменой собственного бронирования
UML Use Case: The employee is associated with checking availability, creating a reservation, confirming, and canceling their own booking.

Figure 4. Lines connect the participant to the goals; they do not define the sequence of HTTP calls.

It's not advisable to turn this diagram into a list of all service methods. Saving the record in the database is an implementation action, while the employee needs the result of the booking. Authentication is considered a precondition for the considered actions.

LLM easily builds such a schema from the list of goals. The benefit of the review lies in checking the boundaries: ensuring the user hasn't gained the ability to confirm others' requests, cancellation hasn't disappeared, and no additional equipment management has been added, which isn't part of the current task. The general notation of these and subsequent behavioral diagrams is defined by UML.

6. C4 Context: Defining the system's boundaries

The next question is where our responsibility ends. On the Context diagram, there are an employee, a booking system, and an external identity provider. We use existing accounts, and the reservation rules belong to the system being created.

C4 Context: сотрудник использует систему бронирования, которая доверяет внешнему провайдеру идентификации
C4 Context: the employee uses a reservation system that trusts an external identity provider

Figure 5. System context: user, target system, and external dependency.

Context is useful to discuss with people who don't need server code details. They may notice the missing external dependency or incorrectly defined responsibility. For example, if the equipment is actually provided by a separate service, its involvement becomes a new requirement.

7. C4 Container: distribute responsibility

We will break down the system into three parts: web interface, reservation API, and relational database. The interface displays data and sends commands. The API checks the user and the rules. The database stores the state and participates in ensuring atomicity.

C4 Container: сотрудник работает с веб-интерфейсом; интерфейс вызывает API по HTTPS и JSON; API обращается к базе по SQL и проверяет токены внешнего провайдера
C4 Container: the employee works with the web interface; the interface calls the API via HTTPS and JSON; the API accesses the database via SQL and checks the tokens from the external provider

Figure 6. C4 Container — application or data store.

On the diagram, we indicate the protocols and save the names for the future Sequence. LLM helps to assemble and draw the architecture. For the selected behavior, here it is sufficient to have one server application. The purpose of this level is more detailed in the C4 Container documentation.

8. State Machine: understanding the lifecycle

The reservation has four states: temporary reservation HELD, confirmed reservation CONFIRMED, cancellation CANCELLED, and expiration EXPIRED. From the temporary reservation, you can transition to any of the three other states. A confirmed reservation can be canceled. Cancellation and expiration complete the life cycle within our example.

UML State Machine: HELD переходит в CONFIRMED при подтверждении до срока, в EXPIRED при достижении срока или в CANCELLED при отмене; CONFIRMED также можно отменить
UML State Machine: HELD transitions to CONFIRMED upon confirmation before the deadline, to EXPIRED upon reaching the deadline, or to CANCELLED upon cancellation; CONFIRMED can also be canceled

Figure 7. Transition conditions are more important than the list of statuses itself. Exactly on the deadline boundary, confirmation is no longer permissible.

To prepare the diagram, a short task is sufficient:

According to the rules BR-02, BR-05, and BR-07 of version equipment-reservation-v1, build the State Machine in PlantUML. Show the transition conditions. Describe repeated actions separately. Do not add new states. If the rule is ambiguous, list the questions before the diagram.

The textual diagram can be easily edited via LLM and stored alongside the documents. Syntax and rendering are automated; checking the meaning remains the task of the review. PlantUML State Diagram description.

9. Sequence: check the interaction of participants

Now let's track the creation of a reservation: the web interface sends parameters, the API checks them, accesses the database, creates a record, and returns the result. Participant names are taken from Container, fields from ERD, and conditions from requirements.

UML Sequence: запрос создания резерва, начало транзакции, блокировка оборудования, чтение пересекающихся броней и ветки ответа 409 или создания с ответом 201
UML Sequence: request for creating a reservation, start of transaction, equipment lock, reading overlapping reservations, and branches for response 409 or creation with response 201

Figure 8. The check and recording are protected by a single transaction and serialization of equipment operations.

LLM is useful to ask to find messages without a handler, transaction breaks and alternative branches. On review, we also check the moment of response: 201 is sent after successful transaction completion.

10. Activity: we reveal the algorithm and branches

Sequence answers the question "who interacts with whom", Activity — "what decisions are made". In the algorithm of creating a reservation, we check the input data, then in the protected operation, we determine blocking reservations and search for overlaps.

UML Activity создания резерва: проверка данных, атомарная область с блокировкой оборудования, проверка пересечения, ответ 409 либо создание HELD и ответ 201
UML Activity for creating a reservation: data check, atomic area with equipment locking, overlap check, response 409 or creation of HELD and response 201

Figure 9. The expired HELD is excluded from blocking reservations until the overlap check is performed.

Conditions are conveniently written without being tied to a programming language:

overlap = existing.startsAt < requested.endsAt and requested.startsAt < existing.endsAt blocks = status == CONFIRMED or (status == HELD and now < holdExpiresAt)

LLM can expand the rules in Activity and offer examples for each branch. When reviewing, it is important to verify the sequence of actions with Sequence. Beautiful pictures describing different algorithms can only increase ambiguity. The same atomic area should be preserved in both representations.

11. OpenAPI: we solidify the external interface

When the behavior is clarified, we fix the HTTP contract. The tutorial document includes creating a reservation, confirmation, and cancellation. Equipment inspection and the entire process of entering it are not included. We are using OpenAPI 3.0.3, as in the original presentation; this is a choice of example format, not a recommendation to consider it the latest version.

To create a reservation, you need equipmentId, startsAt, and endsAt. The owner and reservation period are assigned by the server. Neither userId nor the desired client status is transmitted. A short fragment of the operation looks like this:

/api/v1/reservations: post: operationId: createReservation security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateReservation' responses: '201': description: Temporary reservation created content: application/json: schema: $ref: '#/components/schemas/Reservation' '409': $ref: '#/components/responses/Conflict'

This is a fragment; schema definitions and other responses can be found in the full OpenAPI tutorial. Successful confirmation and cancellation return 200. Incorrect parameters — 400, absence of valid authentication — 401. For unknown or foreign reservations, 404 is used; conflict of time or status — 409 with a distinguishable reason code.

The contract allows for independent discussion of frontend, backend, and tests. LLM can prepare client types and handlers, verify examples against schemas. However, the description of Bearer does not implement token verification, and a correct JSON does not prove the absence of double booking. These responsibilities remain in the rules and implementation. The assignment and structure of HTTP contracts are defined by the OpenAPI specification.

12. Markdown specification: collecting a consistent project

By this point, several related representations have appeared. Now we compile a document through which a developer or agent can navigate from the goal to a specific solution: boundaries, terms, rules, model, diagrams, contract, accepted constraints, and version.

Initial requirements explained what we wanted. The final Markdown specification records the consistent structure of the selected scenario. There is no need to manually rewrite the entire OpenAPI into it: references and explanations of decisions that cannot be inferred from the request structure are sufficient.

In the applications, there is a specification for the example version equipment-reservation-v1. This is a designation of the edition of the training material, not a reference to an existing Git tag. For example, it clearly separates the stored status and the actual expiration based on server time.

LLM helps check consistency: are all states present in the model, does the response match the algorithm branch, is there a rule without a scenario?

13. BDD Scenarios: Agreeing on the Observable Result

Before writing code, we define specific initial data, action, and expected result. For example, in Gherkin terms:

# language: ru Feature: Equipment Reservation Scenario: Confirmation Exactly at the Expiration Boundary Given my reservation is in the state HELD And its expiration time is «2026-11-02T09:10:00Z» And the server time is equal to «2026-11-02T09:10:00Z» When I confirm the reservation Then the API returns a 409 status And the error code equals «RESERVATION_EXPIRED» And the reservation does not block the equipment

Also needed are successful creation, intersection, adjacent intervals, cancellation, repeated commands, and access to another's reservation. For competition, we fix the result: from two simultaneous overlapping requests to free equipment, one gets 201, the other — 409; the blocking record remains one. The winner is not predetermined.

Scenarios are stored in a separate file. Until they have step definitions and an application, they are agreed-upon examples, not executed automated tests. Their automation is assigned to LLM when implementing the corresponding step.

14. Code and tests: one task, one review, next step

Now, the implementation can be passed to the agent. A large feature is broken down so that the result of each task can be checked separately: model and transitions; atomic reservation; confirmation and cancellation; HTTP API; web interface; final check of user scenarios. Tests are added at each step, not deferred until the last moment.

Последовательный цикл: небольшая задача передаётся LLM, затем выполняются проверки и ревью человеком; ошибка возвращает текущий шаг на исправление, принятый результат открывает следующий шаг
Sequential cycle: a small task is passed to LLM, then checks and review are performed by a human; an error returns the current step for correction, the accepted result opens the next step

Figure 10. A human checks the result of the current step before moving to the next. Changes in business rules are returned for project agreement.

For example, a task for the reservation stage:

Implement atomic creation of a temporary reservation. Base: equipment-reservation-v1 — reservation-spec.md, openapi.yaml, 03-erd.puml, 08-sequence.puml, 09-activity.puml and scenarios reservations.feature with the tag @create. All materials are located in assets/. Check the consistency of the input materials. Follow the selected serialization of equipment operations. Automate creation, conflict, adjacent intervals, Lock expiration and two concurrent requests. Do not change the contract and scenario expectations for the sake of green tests. Imagine the changes, the results of the checks, and the open questions. After that, wait for the review; do not start the next stage.

This is a task for a future project: the attached package does not include the service implementation. In a real repository, technology, check teams, and the latest revision of the materials are added to it.

It remains for a person to check the rule compliance and the engineering solution's reasonableness. Fast tests with controlled time are useful for domain logic. Concurrency and locks are checked on the selected database: an imitation storage cannot confirm its transactional guarantees. Then the HTTP contract and user path are checked.

If the code is wrong, we fix the code. If a project gap is found, we first agree on the change of schemas, rules, and scenarios. A small-step review helps localize the problem before the next part of the feature grows over it.

The main effort is in the middle of the process

In large development, it is useful to focus as much as possible where the solution is already sufficiently precise and changing it is still relatively simple: on data models, UML diagrams, C4 boundaries, and contracts. Conversations provide the material for these decisions. Code embodies them. Markdown connects them, and scenarios make expectations testable.

LLM lowers the threshold for schema preparation: describe the rule in plain language, get the diagram text, review the result, and make corrections. Time is primarily spent on meaning—what transition is allowed, who is responsible for the operation, what is considered a conflict. Exactly these decisions are especially costly to recover from already written code.

When the schema, data, and contract are aligned, the agent has fewer reasons to guess behavior. Implementation can be handed out sequentially as small tasks, leaving decision-making and review to the person. This does not automatically make errors negligible, but it allows for earlier detection of discrepancies and limits the scope of rework.

Educational applications

To work with the example, save the application content into files with the specified names in the assets/ directory. Text sources of diagrams are included in the specification application.

OpenAPI 3.0.3 — openapi.yaml
openapi: 3.0.3 info: title: Equipment Reservation — Educational HTTP Contract version: 1.0.0 description: 'Edition: equipment-reservation-v1. Coverage: creation, confirmation and cancellation. Viewing availability, login, operational errors, and equipment issuance are not described. Business rules and model are in reservation-spec.md. This is a contract project, the service is not implemented.' paths: /api/v1/reservations: post: operationId: createReservation summary: Create a temporary reservation description: 'BR-01—BR-04. Atomically check for overlap and create HELD for 10 minutes. Owner is taken from the verified identity. A repeated POST does not have a special idempotency key: a overlapping repeat when a first reservation is active will receive 409 EQUIPMENT_BUSY.' security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateReservation' example: equipmentId: eq-042 startsAt: '2026-11-02T10:00:00Z' endsAt: '2026-11-02T11:00:00Z' responses: '201': description: Temporary reservation created content: application/json: schema: $ref: '#/components/schemas/Reservation' example: id: res-001 equipmentId: eq-042 startsAt: '2026-11-02T10:00:00Z' endsAt: '2026-11-02T11:00:00Z' status: HELD holdExpiresAt: '2026-11-02T09:10:00Z' '400': &id001 $ref: '#/components/responses/BadRequest' '401': &id002 $ref: '#/components/responses/Unauthorized' '404': &id003 $ref: '#/components/responses/NotFound' '409': &id004 $ref: '#/components/responses/Conflict' /api/v1/reservations/{reservationId}/confirm: post: operationId: confirmReservation summary: Confirm reservation description: BR-05—BR-07. HELD is confirmed only if now < holdExpiresAt. CONFIRMED is returned unchanged with 200. EXPIRED, including HELD with expired expiry time, returns 409 RESERVATION_EXPIRED. CANCELLED returns 409 INVALID_STATE. security: - bearerAuth: [] parameters: - name: reservationId in: path required: true schema: &id005 type: string minLength: 1 maxLength: 80 pattern: ^[A-Za-z0-9_-]+$ responses: '200': description: Operation completed content: application/json: schema: $ref: '#/components/schemas/Reservation' example: id: res-001 equipmentId: eq-042 startsAt: '2026-11-02T10:00:00Z' endsAt: '2026-11-02T11:00:00Z' status: CONFIRMED holdExpiresAt: '2026-11-02T09:10:00Z' '400': *id001 '401': *id002 '404': *id003 '409': *id004 /api/v1/reservations/{reservationId}/cancel: post: operationId: cancelReservation summary: Cancel reservation description: BR-05—BR-07. Active HELD and CONFIRMED transition to CANCELLED. Re-cancellation of CANCELLED returns 200 unchanged. EXPIRED, including HELD with expired expiry time, returns 409 RESERVATION_EXPIRED. security: - bearerAuth: [] parameters: - name: reservationId in: path required: true schema: *id005 responses: '200': description: Operation completed content: application/json: schema: $ref: '#/components/schemas/Reservation' example: id: res-001 equipmentId: eq-042 startsAt: '2026-11-02T10:00:00Z' endsAt: '2026-11-02T11:00:00Z' status: CANCELLED holdExpiresAt: '2026-11-02T09:10:00Z' '400': *id001 '401': *id002 '404': *id003 '409': *id004 components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: API checks the signature, issuer, audience, and token expiration. Local user is determined by the issuer/subject pair; userId from the request is not accepted. schemas: CreateReservation: type: object additionalProperties: false required: - equipmentId - startsAt - endsAt description: 'BR-01: startsAt < endsAt. Date comparison and overlap checks are performed by the server.' properties: equipmentId: *id005 startsAt: &id006 type: string format: date-time pattern: Z$ description: UTC time; RFC 3339 string with Z suffix. endsAt: *id006 Reservation: type: object additionalProperties: false required: - id - equipmentId - startsAt - endsAt - status - holdExpiresAt properties: id: *id005 equipmentId: *id005 startsAt: *id006 endsAt: *id006 status: type: string enum: - HELD - CONFIRMED - CANCELLED - EXPIRED description: 'Effective status: HELD with now >= holdExpiresAt is considered EXPIRED.' holdExpiresAt: type: string format: date-time pattern: Z$ description: Original hold period; after CONFIRMED or CANCELLED, it is retained for history and no longer restricts these states. Error: type: object additionalProperties: false required: - code - message properties: code: type: string enum: - INVALID_REQUEST - UNAUTHENTICATED - EQUIPMENT_NOT_FOUND - RESERVATION_NOT_FOUND - EQUIPMENT_BUSY - RESERVATION_EXPIRED - INVALID_STATE message: type: string description: Explanation for a human; client branches by code. responses: BadRequest: description: Incorrect parameters, extra fields, or startsAt >= endsAt. content: application/json: schema: $ref: '#/components/schemas/Error' example: code: INVALID_REQUEST message: Incorrect parameters, extra fields, or startsAt >= endsAt. Unauthorized: description: Missing valid authentication. content: application/json: schema: $ref: '#/components/schemas/Error' example: code: UNAUTHENTICATED message: Missing valid authentication. headers: WWW-Authenticate: schema: type: string example: Bearer description: Authentication scheme. NotFound: description: Equipment not found; reservation does not exist or belongs to another user. content: application/json: schema: $ref: '#/components/schemas/Error' example: code: RESERVATION_NOT_FOUND message: Equipment not found; reservation does not exist or belongs to another user. Conflict: description: Interval conflict or invalid action on the state. content: application/json: schema: $ref: '#/components/schemas/Error' examples: EQUIPMENT_BUSY: value: code: EQUIPMENT_BUSY message: Interval is occupied. RESERVATION_EXPIRED: value: code: RESERVATION_EXPIRED message: Reservation period has expired. INVALID_STATE: value: code: INVALID_STATE message: Action is invalid in the current state.
Specification and diagram sources — reservation-spec.md

Equipment reservation: specification of a learning example

Edition: equipment-reservation-v1. Status: a learning project agreed upon within the article. This is not an implemented service and not a Git tag. All materials in this package belong to this edition.

Purpose and Scope

The employee reserves a specific piece of equipment, confirms or cancels their own reservation. Interval conflicts, reservation duration, and team competition are considered. Payment, reservation extension, check-out, return, equipment management, and user registration are out of scope.

Availability check is part of the user scenario, but its HTTP contract in the attached OpenAPI is not described. Availability on the screen is informational: guarantee appears only after a successful reservation. The educational project does not establish production SLOs, request limits, and infrastructure failure handling policies.

Requirements and Rules

| ID | Rule | | — | — | | BR-01 | Interval [startsAt, endsAt); startsAt < endsAt. Intersection: a.startsAt < b.endsAt && b.startsAt < a.endsAt. Adjacent intervals are allowed. All dates are transmitted in UTC with Z. The educational contract does not introduce additional restrictions regarding the current date. | | BR-02 | Upon successful creation, the state is HELD, createdAt = now, holdExpiresAt = now + 10 minutes. Owner and time are assigned by the server. | | BR-03 | Identical equipment is blocked by overlapping CONFIRMED and HELD reservations when now < holdExpiresAt. CANCELLED and expired reservations do not block. | | BR-04 | Conflict check and creation are atomic. All changes to reservations for the same equipment are serialized by locking its row. Checks read the current state after acquiring the lock. | | BR-05 | HELD reservations with now >= holdExpiresAt have an effective state of EXPIRED, even if the stored field is still HELD. Confirmation and cancellation return 409 RESERVATION_EXPIRED. The expired reservation does not occupy the interval. | | BR-06 | API checks the token and matches (issuer, subject) to the local user. Only your own reservations can be confirmed or canceled. Lack of valid authentication — 401; unknown and foreign reservation — same 404 RESERVATION_NOT_FOUND. | | BR-07 | The active HELD reservation is confirmed to CONFIRMED or canceled to CANCELLED. CONFIRMED can be canceled to CANCELLED. Repeating confirm for CONFIRMED and cancel for CANCELLED returns 200 without change. Confirm for CANCELLED — 409 INVALID_STATE. Restoration from CANCELLED and EXPIRED is not supported. |

Processing order: authentication → format check → resource search and owner check → secure state check and change. State errors are not disclosed for foreign reservations. confirm/cancel commands do not have a request body. Repeated create does not inherit idempotency from confirm/cancel commands: upon an active first reservation, a repeat on overlapping interval returns 409 EQUIPMENT_BUSY.

Data model

ERD · source.

— User: id (PK), authIssuer, authSubject; pair (authIssuer, authSubject) is unique. — Equipment: id (PK), inventoryNumber (unique), name. — Reservation: id (PK), userId (FK), equipmentId (FK), startsAt, endsAt, createdAt, holdExpiresAt, status. — All listed fields are mandatory. Each reservation is associated with exactly one user and one equipment; each of them can have zero to many reservations. — status: HELD, CONFIRMED, CANCELLED, EXPIRED. The reservation period is preserved in the history after confirmation or cancellation and does not restrict these states. — Public response does not contain userId, external identity, and createdAt; storage schema and HTTP DTO differ.

Architecture and Behavior

Use Case · C4 Context · C4 Container.

Employee uses the web interface. The web interface communicates with the booking API via HTTPS/JSON with a Bearer token. The external identity provider handles login; the API receives metadata and keys for token verification. The API queries the booking database via SQL. Local owner verification remains the responsibility of the API. C4 describes architecture separately from UML.

State Machine · Sequence · Activity.

Creation operation:

  1. Check authentication, format, and required fields, compare start and end; find equipment.

  2. Start a transaction and obtain a row lock on the equipment. Equipment deletion is not provided in the educational scenario; in the full product, existence is checked and within the transaction.

  3. Get the current server time now after waiting for the lock.

  4. Read current bookings for this equipment. The database and isolation level must ensure visibility of results from previously completed operations after obtaining the lock; an outdated snapshot is not allowed.

  5. Check BR-03. In case of conflict, roll back the transaction, release the lock, and return 409 EQUIPMENT_BUSY.

  6. Create HELD according to BR-02. Commit the transaction and only return 201 with the reservation after a successful commit.

Confirm/cancel use the same serialization by equipment; after relocking, the record is read again and the effective state is checked. Time and state are evaluated within the protected operation. Background materialization of EXPIRED is not required for correctness and should also not overwrite concurrent confirmation.

Timeouts, deadlocks, and connection loss require a separate policy for the full product. Automatic retry of create after an unknown result is not considered a safe default solution. The example describes normal transaction completion and business conflicts.

Public HTTP contract

OpenAPI 3.0.3, document version 1.0.0.

| Operation | Success | Business conflicts | | — | — | — | | POST /api/v1/reservations | 201, HELD | 409 EQUIPMENT_BUSY | | POST /api/v1/reservations/{reservationId}/confirm | 200, CONFIRMED | 409 RESERVATION_EXPIRED / INVALID_STATE | | POST /api/v1/reservations/{reservationId}/cancel | 200, CANCELLED | 409 RESERVATION_EXPIRED |

Common errors: 400 INVALID_REQUEST, 401 UNAUTHENTICATED, 404 EQUIPMENT_NOT_FOUND during creation and 404 RESERVATION_NOT_FOUND for unknown/foreign reservations. The error contains code and message; client logic uses code.

Example of creation: server time 2026-11-02T09:00:00Z, equipment eq-042, interval 10:00–11:00Z of the same date. Result: HELD, duration 09:10:00Z. Exactly at 09:10:00Z, the unconfirmed reservation no longer blocks the interval.

Scenarios and rule compliance

BDD on Gherkin. Scenarios have tags for rules and steps. Time is controlled by a test stand, example dates do not depend on the actual day of launch.

| Check | Rules | | — | — | | Creation and assignment of time | BR-01, BR-02 | | Intersection and adjacent intervals | BR-01, BR-03 | | Parallel requests | BR-03, BR-04 | | Expiration and delay of background refresh | BR-03, BR-05 | | Confirmation, cancellation, and repetition of commands | BR-05, BR-07 | | Unauthorized request, foreign and unknown booking | BR-06 |

Sequence of implementation for the future project

  1. Model, intersection functions and transitions; fast checks with time control.

  2. Storage and atomic booking; scenarios @create, including concurrency on a real selected DBMS.

  3. Confirmation and cancellation; scenarios @confirm, @cancel, owner check and repetitions.

  4. HTTP adapters via OpenAPI; format and status checks. Business layer checks on previous steps work without HTTP adapters if needed.

  5. Web interface: free time, commands, clear display of conflicts and time. API reading needs to be separately agreed before this step, which is missing in the tutorial document.

  6. Final check of user path and artifact consistency.

After each step — checks and human review. Requirement changes precede changes to dependent contracts, scenarios, and code. The presence of a BDD file does not mean that tests have been completed: the service and step definitions are not included in this package.

Text sources of diagrams

03-erd.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 hide circle hide methods entity "User\nПользователь" as U { * id : identifier <> - * authIssuer : string * authSubject : string } entity "Equipment\nЕдиница оборудования" as E { * id : identifier <> - * inventoryNumber : string <> * name : string } entity "Reservation\nБронирование" as R { * id : identifier <> - * userId : identifier <> * equipmentId : identifier <> } * startsAt : UTC timestamp * endsAt : UTC timestamp * holdExpiresAt : UTC timestamp * createdAt : UTC timestamp * status : enum } U ||-down-o{ R : holds E ||-down-o{ R : reserves U -[hidden]right- E @enduml

04-use-case.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 actor "Employee" as U rectangle "Booking System" { usecase "View\nAvailability" as View usecase "Create\nTemporary Reservation" as Hold } usecase «Confirm\nyour reservation» as Confirm usecase «Cancel\nyour reservation» as Cancel View -[hidden]down- Hold Hold -[hidden]down- Confirm Confirm -[hidden]down- Cancel } U -right- View U -right- Hold U -right- Confirm U -right- Cancel @enduml

05-c4-context.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 actor «Employee\n[Person]» as U rectangle «Booking System\n[Software System]\n\nAvailability, reserves,\nconfirmation and cancellation» as S rectangle «Identity Provider\n[External Software System]» as I #E8E9ED U -down-> S : Reserves equipment S -down-> I : Uses external identity U -right-> I : Logs into account

06-c4-container.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 actor «Employee\n[Person]» as U rectangle «System\nReservation» { rectangle «Web Interface\n[Container: browser application]\n\nSelect time and command» as W rectangle «Booking API\n[Container: server application]\n\nUser and rules verification» as A database «Booking Database\n[Container: relational DB]\n\nData, transactions, locks\n» as D W -down-> A : Commands / Responses\n[HTTPS / JSON]\nBearer token A -down-> D : Reading and modification\n[SQL] } rectangle «Provider\nidentification\n[External System]» as I #E8E9ED U -down-> W : Uses W -right-> I : Login\n[OIDC / HTTPS] A -right-> I : Verification keys\n[HTTPS] @enduml

07-state-machine.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 state "Temporary Hold\nHELD" as H state "Confirmed\nCONFIRMED" as C state "Cancellation\nCANCELLED" as X state "Expired\nEXPIRED" as E [*] --> H : create H -down-> C : confirm\n[now <\nholdExpiresAt] H -right-> E : time expired\n[now >=\nholdExpiresAt] H -left-> X : cancel\n[now <\nholdExpiresAt] C -left-> X : cancel @enduml

08-sequence.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 hide footbox skinparam maxMessageSize 350 participant «Web Interface» as W participant «Booking API» as A database «Booking Database» as D note over W,D Precondition: authentication and parameters are checked; equipment exists. The creation operation is shown; end note W -> A : POST /api/v1/reservations activate A group One transaction / serialization per equipment A -> D : BEGIN; block the Equipment row D —> A : Lock acquired A -> D : now = server time A -> D : Read blocking reservations\nand check for overlap D —> A : Check result alt There is an overlap A -> D : ROLLBACK; release lock D —> A : Transaction completed A —> W : 409 EQUIPMENT_BUSY else Interval is free A -> D : INSERT Reservation: HELD\nholdExpiresAt = now + 10 minutes A -> D : COMMIT; release lock D —> A : Changes saved A —> W : 201 Reservation end end deactivate A @enduml

09-activity.puml

@startuml !pragma layout smetana skinparam backgroundColor #F7F6F2 skinparam defaultFontName DejaVu Sans skinparam defaultFontSize 23 skinparam shadowing false skinparam roundcorner 12 skinparam ArrowColor #315DF5 skinparam ArrowThickness 2 skinparam LineThickness 2 skinparam defaultFontColor #182020 skinparam classBackgroundColor #E9EEFF skinparam classBorderColor #315DF5 skinparam classAttributeFontSize 22 skinparam classFontSize 25 skinparam rectangleBackgroundColor #E9EEFF skinparam rectangleBorderColor #315DF5 skinparam rectangleFontSize 23 skinparam packageBackgroundColor #F7F6F2 skinparam packageBorderColor #9BAACD skinparam stateBackgroundColor #E9EEFF skinparam stateBorderColor #315DF5 skinparam stateFontSize 24 skinparam usecaseBackgroundColor #E9EEFF skinparam usecaseBorderColor #315DF5 skinparam activityBackgroundColor #E9EEFF skinparam activityBorderColor #315DF5 skinparam activityDiamondBackgroundColor #FFFFFF skinparam activityDiamondBorderColor #315DF5 skinparam participantBackgroundColor #E9EEFF skinparam participantBorderColor #315DF5 skinparam sequenceLifeLineBorderColor #9BAACD skinparam sequenceGroupBorderColor #315DF5 skinparam sequenceGroupBackgroundColor #F7F6F2 skinparam noteBackgroundColor #FFFFFF skinparam noteBorderColor #9BAACD skinparam nodesep 55 skinparam ranksep 65 start :Authenticated request; if (Parameters are correct?) then (no) :400 INVALID_REQUEST; stop else (yes) endif if (Equipment exists?) then (no) :404 EQUIPMENT_NOT_FOUND; stop else (yes) endif partition «Atomic operation» { :Start transaction;\nlock the Equipment row; :Get server time now; :CONFIRMED locks without checking duration;\nHELD — only when now < holdExpiresAt; if (Is there an overlap in intervals?) then (yes) :Rollback transaction;\nrelease lock; :409 EQUIPMENT_BUSY; stop else (no) :Create HELD;\nholdExpiresAt = now + 10 minutes; :Commit transaction;\nrelease lock; endif } :201 Reservation; stop @enduml
BDD Scenarios — reservations.feature
# language: ru @equipment-reservation-v1 Feature: Equipment Reservation Learning expectations; service and step implementation is missing. All times are UTC, server time is managed by the test stand. Background: Assume equipment «eq-042» exists. And I am authenticated as user «user-a» And the server time is equal to «2026-11-02T09:00:00Z» And the equipment has no reservations @create @BR-01 @BR-02 Scenario: Creating a free reservation When I create a reservation «eq-042» from «2026-11-02T10:00:00Z» to «2026-11-02T11:00:00Z» Then API returns status 201 And the reservation status is equal to «HELD» And the reservation period is equal to «2026-11-02T09:10:00Z» And the saved record's owner is «user-a» @create @BR-01 @BR-03 Scenario: Intersection with a confirmed reservation Assume equipment is occupied by a confirmed reservation from «2026-11-02T10:00:00Z» to «2026-11-02T11:00:00Z» When I create a reservation «eq-042» from «2026-11-02T10:30:00Z» to «2026-11-02T11:30:00Z» Then API returns status 409 And the error code is equal to «EQUIPMENT_BUSY» And the new reservation is not created @create @BR-01 @BR-03 Scenario: Adjacent intervals do not overlap Assume equipment is occupied by a confirmed reservation from «2026-11-02T10:00:00Z» to «2026-11-02T11:00:00Z» When I create a reservation «eq-042» from «2026-11-02T11:00:00Z» to «2026-11-02T12:00:00Z» Then API returns status 201 @create @BR-03 @BR-05 Scenario Structure: Reservation period affects availability without background update Assume a HELD reservation is saved for «eq-042» from «2026-11-02T10:00:00Z» to «2026-11-02T11:00:00Z» And its period expires at «2026-11-02T09:10:00Z» And the background handler did not update the status And the server time is equal to «