Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Acceptance Criteria and Edge Cases for Note, Review, Feedback, and Reminder Workflows

Hello! This course will build a note-review system from its behavior outward: first defining what it must do, then designing its components, data, scheduling, delivery, and tests. This first lesson establishes the behavioral contract. Before choosing tables or endpoints, you will specify outcomes that can be checked for note ingestion, the daily review queue, recall feedback, and reminders.

Plan on about 40 minutes: roughly four minutes of video, eight minutes of reading, and the remaining time working through the system examples. The examples below form a proposed specification, not a claim that every product decision has already been made. Where a choice matters, we will state it explicitly.


From a feature idea to a checkable outcome

“Import my notes” describes a capability, but it does not say what happens when a note changes, appears twice, or cannot be read. Acceptance criteria close that gap: they describe the conditions under which a feature is considered working. An edge case is a condition near a boundary or outside the ordinary path that may change the expected behavior.

A useful structure is Given–When–Then:

  • Given a known starting state;
  • When one event occurs;
  • Then an observable result follows.

A result such as “the database is updated correctly” is too vague. “The next daily queue contains the revised note title exactly once” identifies something that can be checked.

User Stories and Acceptance Criteria EXAMPLE (Agile Story Tutorial)

Watch “User Stories and Acceptance Criteria EXAMPLE (Agile Story Tutorial)” from The Business Analysis Doctor for the purpose of acceptance criteria and the Given–When–Then structure.

Watch confirmation to see why a short feature description needs agreed checks. Then watch story boundaries, scenario structure, and checkable outcomes. Focus on the distinction between describing a desired feature and specifying evidence that it works.

The gardening illustration separates existing conditions (seeds), an action (watering), and an expected outcome (growth). For software criteria, the outcome must be stated precisely enough to verify.

For instance, “Then the reminder works” does not tell a tester what to inspect. “Then Telegram receives one reminder request for that day’s review window” does. Precision also exposes decisions that would otherwise remain hidden: Which day? What constitutes one window? What if the request times out?


Write scenarios about behavior, not implementation

Gherkin is a structured way to express these examples. Its Scenario Outline form can reuse one scenario with several boundary values. You do not need to adopt a Gherkin test runner to benefit from its discipline; readable scenarios can guide manual checks and later automated tests.

Reference | Cucumber

Read the Cucumber “Gherkin Reference” for its guidance on concrete scenarios, step meanings, observable results, and scenario outlines.

In “Steps,” read the “Example,” “Given,” “When,” “Then,” and “And, But” subsections. In particular, note behavior over interface in “When,” and observable results in “Then.” Next, in “Scenario Outline” and “Examples,” read value-driven examples. Focus on how a different input row can make a boundary test without duplicating the whole scenario.

A strong scenario normally has a specific starting state, a recognizable event, and a result with a clear pass or fail. It should also stand on its own: testing an edited note should not depend on a different scenario having created that note first.

For this lesson’s examples, assume the following working product decisions:

  • Each synced Markdown note has a stable note_id in its metadata. Changing its file path does not change its identity.
  • Queue and reminder examples use UTC as the configured review timezone. A day begins at 00:00 UTC and ends just before the next 00:00 UTC.
  • A daily queue contains pending notes due before the end of that day, including overdue notes. It sorts by due time, then by note_id to break ties.
  • Supported recall ratings are Again, Hard, Good, and Easy. A separate scheduling policy determines the next due time; we are not defining its calculations yet.

These choices make the following criteria verifiable. In a real product discussion, changing one of them means revisiting the affected scenarios.


Workflow 1: note ingestion

The source for ingestion is the locally synchronized note repository. Successful ingestion should make an eligible note available to the review system. Two risks deserve particular attention: identity and partial failure. A retry must not create another copy of the same note; a failed sync must not be mistaken for an intentionally empty repository.

Feature: Ingest synchronized notes

  Scenario: Ingest a new note
    Given the synchronized repository contains one readable Markdown note
    And its metadata contains note_id "n-17" and title "Indexes"
    When ingestion processes the repository
    Then the review system exposes one note with note_id "n-17" and title "Indexes"

  Scenario: Reprocess an edited note
    Given the review system already exposes note_id "n-17" with title "Indexes"
    And the synchronized note with note_id "n-17" now has title "Database indexes"
    When ingestion processes the repository twice
    Then the review system exposes exactly one note with note_id "n-17"
    And its title is "Database indexes"

The second scenario says more than “updates work.” It specifies the identity that remains stable, the changed value, and the count after a retry. Review history associated with that note should also remain associated with it; otherwise an edit could erase the learner’s progress. That can be checked when review-history behavior is available.

Now consider inputs the happy path does not cover. These are edge-case decisions, not merely unusual test data:

Condition Proposed expected behavior
A Markdown file lacks a usable note_id Report that file as invalid; do not create a reviewable note from it.
Two files declare the same note_id Report a conflict; do not silently let one overwrite the other.
Repository synchronization fails Report the ingestion failure and leave previously ingested notes available; do not treat the failure as a request to remove them.
A file is not Markdown Ignore it rather than presenting it as a note.

Each row can become its own Given–When–Then scenario when implemented. Deletion is a separate unresolved product decision: removing a source file might archive a note, hide it from future reviews, or do something else. Do not invent deletion behavior inside an ingestion test without first agreeing on it.


Workflow 2: the daily review queue

“Show today’s reviews” sounds straightforward until “today” and “due” are pinned down. Under our working decisions, the queue at 09:00 UTC on June 12 includes a pending note due yesterday and one due at 23:59 UTC on June 12. It excludes one due exactly at 00:00 UTC on June 13. That midnight value is a boundary test: moving it by even a minute changes its membership.

Feature: View the daily review queue

  Scenario: Include overdue and today-due notes in a stable order
    Given it is 2026-06-12 09:00 UTC
    And pending note "n-02" is due at 2026-06-11 10:00 UTC
    And pending note "n-01" is due at 2026-06-12 23:59 UTC
    And pending note "n-03" is due at 2026-06-13 00:00 UTC
    When the daily review queue is requested
    Then the queue contains "n-02" followed by "n-01"
    But it does not contain "n-03"

  Scenario: No notes are due
    Given it is 2026-06-12 09:00 UTC
    And every pending note is due on or after 2026-06-13 00:00 UTC
    When the daily review queue is requested
    Then the queue is empty

Notice that empty is a valid outcome, not an error. Two more useful edges are notes with identical due times, which must follow the agreed note_id tie-breaker, and a note that has already received feedback and is no longer pending for today. These checks prevent a queue from changing order unpredictably or continuing to offer a completed review.

The exact time examples serve a purpose: “due tomorrow” can mean different things across timezones, but “due at 2026-06-13 00:00 UTC” is unambiguous under this specification. A later design may choose a different timezone or queue cutoff; the criteria must then change with it.


Workflow 3: recall feedback

Feedback is not just a button response. An accepted rating must have an observable result, and a repeated request must not accidentally count as a second review. To distinguish a retry from a genuinely new submission, this proposed contract gives each submission a unique submission key.

For the first scenario below, assume the scheduling policy fixture returns 2026-06-13 09:00 UTC for this particular accepted rating. That is a known expected result for this test, not a scheduling formula.

Feature: Submit recall feedback

  Scenario: Accept feedback for a pending review
    Given note "n-17" is pending in today's queue
    And the scheduling policy returns 2026-06-13 09:00 UTC for a Good rating
    When a Good rating is submitted for "n-17" with submission key "s-81"
    Then the response reports next due time 2026-06-13 09:00 UTC
    And a fresh request for today's queue no longer contains "n-17"

  Scenario: Retry the same submission
    Given the Good rating for "n-17" with submission key "s-81" was accepted
    When that same submission is sent again with key "s-81"
    Then the response reports the same next due time
    And the review is counted only once

“Counted only once” needs evidence. An integration test could inspect recorded review events, while a user-facing review history could show one accepted rating. The response and subsequent queue request provide additional observable checks; the scenario should not rely solely on an assertion that an internal function ran.

Other feedback edges require explicit outcomes. A rating outside Again, Hard, Good, and Easy should be rejected without changing the review. A submission for a note that is not pending should not create a new review merely because the note exists. Reusing a submission key with a different rating should be reported as a conflict, rather than silently changing the result of the original submission. The precise rejection response can be settled when the API contract is designed, but “no review change” is already an important acceptance condition.


Workflow 4: daily reminders

A reminder has two distinct questions: should one be attempted? and was it delivered? This proposed contract attempts a reminder at 09:00 UTC only if that day’s queue is nonempty. Its duplicate-prevention promise is deliberately narrow: at most one request is submitted to Telegram for a given daily window.

Feature: Send the daily review reminder

  Scenario: A due queue triggers one reminder attempt
    Given it is the 2026-06-12 09:00 UTC reminder window
    And today's review queue contains two notes
    When the reminder window is processed twice
    Then Telegram receives exactly one reminder request for that window

  Scenario: An empty queue triggers no reminder
    Given it is the 2026-06-12 09:00 UTC reminder window
    And today's review queue is empty
    When the reminder window is processed
    Then Telegram receives no reminder request

A timeout creates a harder edge case. Telegram might have received a request even if the application did not receive confirmation. Automatically retrying could produce a duplicate, so “at most one request” is not the same promise as “the user definitely receives one message.” Under this proposed contract, a failed or uncertain attempt is reported as such and is not automatically submitted again in the same window. An operator can inspect the attempt status; delivery is not claimed when it is unknown. If the product instead requires automatic retries, its duplicate and delivery guarantees need a separate decision.


Check the contract before designing the machinery

Across all four workflows, a useful review of your criteria asks:

  1. Is the starting state specific? Identify the note, due time, timezone, or reminder window that matters.
  2. Is the event clear? Distinguish a first ingestion from a retry, or a new feedback submission from a duplicate.
  3. Can the result pass or fail? State returned values, counts, order, exclusions, or externally visible requests rather than “works correctly.”
  4. Have important boundaries been covered? Check missing or conflicting IDs, the next-day cutoff, invalid ratings, empty queues, and uncertain reminder delivery.
  5. Are undecided policies visible? A test should not quietly settle note deletion, scheduling calculations, or retry guarantees by accident.

The main takeaway is that acceptance criteria are a behavioral contract, not an implementation plan. Concrete examples reveal the decisions that a broad feature description hides, while edge cases expose where that contract might break. In the next lesson, you will assign responsibility for fulfilling this contract to the system’s components—without confusing what must happen with which component performs it.

Can't find a good explanation? Sign up and we'll make it for you

Sign up