Shared Codebook for Qualitative Coding
Set who may add, rename, merge, or retire codes, how a change reaches excerpts already coded, and where Potato logs code definitions and changes.
Potato's codebook is the list of codes a project's coders apply. Potato keeps the codebook in the project database instead of the YAML config. A scheme opts in with codebook: true, and the scheme's options then come from the codebook. A coder can add a code in the middle of a session without anyone editing the config or restarting the server. Codes can sit under a parent code, and each code's entry has typed fields that include a definition, inclusion and exclusion rules, and examples. Potato stamps every saved annotation with the codebook revision in effect at the moment of saving. The codebook arrived with QDA Mode in Potato 2.6.0 and works in standard annotation, Solo Mode, and QDA Mode.
A codebook in qualitative research lists the codes a team applies and says when each code applies. MacQueen et al. (1998) argue that a codebook with the same fixed structure for every code gives a team of coders a stable frame and improves intercoder agreement. Potato gives every code the same set of typed fields, from a short definition to counter-examples. The codebook development guide covers how a team writes and revises the entries.
Codebook storage and seeding
- Storage. Codes live in
<task_dir>/project.sqlite. Memos and cases share the same project database. The YAML holds only the starting labels. - Seeding. On the first server start, Potato copies the
labelsof every scheme markedcodebook: trueinto an empty codebook. From then on the database is the source of truth, and the server ignores later edits to the YAML labels. - One codebook per project. Every scheme marked
codebook: trueshows the same codes. A project with athemesscheme and aquotesscheme draws both from one list. - Scheme types.
radio,multiselect,select, andspanacceptcodebook: true. - Revisions. Adding, renaming, recoloring, moving, deleting, merging, or splitting a code raises the project's codebook revision by one. Potato records the revision on every annotation saved afterward.
- Where coders see the codebook. A Codebook toggle on the right edge of the annotation page opens a tray listing each code with the code's short definition. The
/codebookpage shows the full codebook, with every field of every code.
Configuration
The config below sets up interview coding for a team in extensible mode. Coders can add a code in the middle of reading, and only an adjudicator may rename or delete codes. The two seed labels use the object form, and an object-form label starts the code with a color and a short definition.
annotation_task_name: Clinic Access Interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/interviews.json
item_properties:
id_key: id
text_key: text
user_config:
allow_all_users: true
codebook_mode: extensible
annotation_schemes:
- annotation_type: span
name: codes
description: Highlight a passage and apply a code
codebook: true
labels:
- name: access barriers
color: "#4682b4"
description: Anything the speaker names as standing between them and care they wanted.
- name: cost concerns
color: "#b44682"
description: Money, insurance, or the fear of a bill.| Option | Default | Description |
|---|---|---|
annotation_schemes[].codebook | false | Takes the scheme's options from the project codebook. |
annotation_schemes[].labels | (none) | Seeds an empty codebook on first start. A label may be a string or an object with name, color, and description (or tooltip). The description becomes the code's short definition. |
codebook_mode | open when qda_mode.enabled or solo_mode.enabled is true, otherwise fixed | Who may change codes: fixed, extensible, or open. Wins over codebook.mode when both are set. Any other value fails validation. |
codebook.mode | same as codebook_mode | The nested spelling of codebook_mode. |
codebook.enabled | unset | Turns the tray, the /codebook page, and the codebook API on or off. Unset, the codebook turns on when a scheme sets codebook: true or under QDA Mode or Solo Mode. A config that sets codebook_mode has the codebook on whatever codebook.enabled says. |
codebook.distiller.include (or include_types) | short_def, definition, use_when, avoid_when, example, counter_example | Which fields of each code go into the text Potato adds to ICL labeling prompts. |
codebook.distiller.include_doc_sections | preamble, general_instructions | Which codebook-wide sections go into the same prompt text. |
codebook.distiller.scope | per_code | per_code adds each code's fields. whole_doc adds only the codebook-wide sections. |
codebook.distiller.procedure | concat | How the prompt text is assembled. llm_summarize and select_icl_examples are accepted and produce the same output as concat in 2.13.1. |
codebook.distiller.max_chars | no limit | Truncates the prompt text to the given number of characters. |
codebook_invivo_key | i | Key that opens the in-vivo composer while text is selected in a codebook-backed span scheme. Only the first character counts. |
qda_mode.codebook.mode passes validation. Potato 2.13.1 does not read the key when deciding who may edit. Under QDA Mode, set codebook_mode to change the default of open.
Who can change codes
What a logged-in coder may do depends on the mode. An adjudicator may make every structural change in extensible and open mode. In fixed mode neither coders nor adjudicators can add, rename, recolor, move, or delete a code. Admins and adjudicators can still merge and split codes from the tray's Curate section.
| Mode | Coders may | Typical use |
|---|---|---|
fixed | apply codes only | a reliability round, crowd work, a codebook agreed before coding |
extensible | add codes | team coding with an agreed core and room for new codes |
open | add, rename, recolor, move, and delete codes, and edit definitions | one analyst, or a small team developing the codebook together |
Potato locks the mode to fixed for any crowdsourcing configuration and logs a warning when the config asked for more. Potato applies the lock to keep recruited annotators from reshaping a shared codebook. The lock covers crowdsourcing.provider with any value except expert, a top-level prolific: or mturk: block, and login.type: prolific or mturk. An unrecognized provider name and an empty provider value lock the mode too.
crowdsourcing.provider: expert keeps the requested mode, because experts are invited by name. login.type: url_direct alone keeps the requested mode and logs a warning. Anyone with the link to such a study can register and then edit an open codebook.
Each kind of change has a separate route in 2.13.1:
| Change | In the browser | Over the API | Who |
|---|---|---|---|
| Add a code | the tray's "Add a code" box, or the in-vivo key | POST /api/codebook | extensible or open |
| Rename, recolor, or move under a parent | no control in 2.13.1 | PATCH /api/codebook/<id> | open, or an adjudicator in extensible |
| Delete a code | no control in 2.13.1 | DELETE /api/codebook/<id> | open, or an adjudicator in extensible |
| Merge one code into another | the tray's Curate section | POST /api/codebook/admin/merge | an admin or adjudicator, in any mode |
| Split a code by coder | the tray's Curate section | POST /api/codebook/admin/split | an admin or adjudicator, in any mode |
| Edit a definition or add an example | the pencil on a code in the tray, or /codebook | PUT /api/codebook/blocks | open, or an adjudicator in extensible. Anyone else's edit is queued for an admin or adjudicator to confirm. |
The tray's add box creates a top-level code. A code goes under a parent through the parent_id field of POST or PATCH.
In-vivo coding from a text selection
On a span scheme marked codebook: true, a coder selects a passage and presses the in-vivo key. A small composer opens with a name taken from the selected words (up to 60 characters), and "Create & code" adds the code and highlights the passage with the new code in one step. While the coder types the name, existing codes with similar names appear as chips. A coder who chooses a chip applies the existing code instead of creating a near-duplicate such as cost beside cost concerns.
Model-proposed changes
A model never changes a shared codebook directly. An agent or a Solo Mode model stages a change with POST /api/codebook/proposals and actor_kind: "model". The change waits in the Curate section as a sentence such as "Merge «cost» into «cost concerns»". An admin or adjudicator confirms or rejects the proposal. A confirmed proposal runs through the same path as a human edit and is marked as model-originated in the change log.
Nesting codes
A code with a parent_id sits under that parent, and a parent can sit under another parent. A move that would place a code under one of the code's own descendants fails with an error. Adding a second code with the same name under one parent fails the same way. Deleting a parent deletes every code under the parent.
The tray and /codebook show the tree. The annotation form shows the codes as one flat list in tree order, parents before children. A form option and a saved annotation both hold only the code's name. The same name under two parents therefore appears twice on the form, and the two codes cannot be told apart afterward. Keep code names unique across the whole codebook.
The codebook CSV export and the .qdpx export both write a codebook-backed scheme's codes as a flat list in 2.13.1. The CSV's parent and color columns are empty for those codes. A code hierarchy needed in another program has to be rebuilt there, or recorded in the code names.
Effect of code changes on saved codings
Potato saves each coding with the code's name as the label, in the coder's user_state.json. A merge or split never deletes a row from the database. Potato applies each codebook change to the codebook alone and never rewrites a saved annotation:
- Adding a code leaves saved codings unchanged.
- Renaming a code changes the form from then on. Codings saved under the old name keep the old name, and exports and agreement statistics count those codings under the old name. Rename before a coding round starts, or recode the affected excerpts.
- Deleting a code removes the code and every code under the code from the form. Saved codings keep the name, and the change log keeps the deleted code's name.
- Merging archives the source code. An archived code leaves the form, and the code's row and history stay in the database. Codings saved with the source code keep the source code's name.
- Splitting by coder names one coder and a new or existing target code. The split is meant for two coders who used one code name for different things, and is designed to move only the named coder's uses. In 2.13.1 the annotation form does not record the per-coder links a split moves. A split therefore creates the target code and archives the source code for every coder whenever the source code has no child codes. Saved codings keep the name each coder chose.
Potato tells coders about changes in two ways. First, a banner on an excerpt saved before new codes were added names the new codes. Second, the tray's Review list holds every excerpt the coder saved before the latest change of any kind. Each excerpt in the list has a Go button. Both signals can be dismissed, and Potato never forces a recode. An admin or adjudicator sees the same list for the whole project at GET /api/codebook/admin/stale.
Definition edits have a separate counter. An edit to a code's short definition, definition, "Use when", or "Avoid when" field raises a semantic revision, and Potato records the semantic revision on every annotation saved afterward. An analyst can then separate codings made before and after a definition changed. Ticking Minor edit (no re-review) in the /codebook editor saves the wording without raising the semantic revision. The Review list in 2.13.1 tracks structural changes only.
Set codebook_mode: fixed for a reliability round, because Potato computes agreement over saved code names and a code renamed mid-round becomes two codes. Inter-Annotator Agreement covers the coefficients. The agreement timeline on /admin/iaa marks each codebook revision against agreement over time. Agreement Drift explains how to read the timeline.
Typed fields in each code's entry
Each code's entry is a list of typed fields. /codebook is the full-page editor, with a version history for every code, a diff between versions, and a restore that saves an older version as a new version. A "Paste markdown" button turns pasted codebook prose into fields by heading and asks the user to choose a type for any section the parser does not recognize. Two people saving the same code at once do not overwrite each other. The second save fails and returns the current text to reconcile against.
| Field | Heading in the editor | Semantic |
|---|---|---|
short_def | Short definition | yes |
definition | Definition | yes |
use_when | Use when | yes |
avoid_when | Avoid when | yes |
example | Examples | no |
counter_example | Counter-examples | no |
rationale | Rationale | no |
background | Background | no |
downstream_usage | Downstream usage | no |
keywords | Keywords | no |
notes | Notes | no |
custom | the user's own heading | no |
The codebook as a whole has four sections outside the codes, titled Preamble, General instructions, Background, and How codes are used downstream. The tray shows each code's short definition, clamped to two lines. When a code has no short definition, the tray shows the full definition instead.
The distiller turns the chosen fields into text that Potato adds to ICL labeling prompts, and rebuilds the text after every codebook change. GET /api/codebook/distilled returns the text a model currently receives. Add a codebook.distiller block to the config above to choose the fields:
codebook:
distiller:
include: [definition, use_when, avoid_when, example]
procedure: concatSeeding and migrating with the CLI
potato codebook adds a code for every label of every codebook: true scheme that the codebook does not have yet, and leaves existing codes alone:
potato codebook config.yamlThe command prints how many codes were created and how many were already present. Codes created by the CLI get the same ID on every machine, because the CLI computes each ID from the project name, the parent, and the code name. Run the CLI before the first server start when IDs need to match across copies of a project, because codes seeded by the server get random IDs. The CLI copies names only. Colors and definitions from object-form labels reach the codebook only through the server's first-start seed.
Because the server seeds only an empty codebook, the CLI is the way to add new YAML labels to a codebook that already has codes. To preview without writing, run the module directly. In 2.13.1, potato codebook --dry-run drops the flag and writes the codes.
python -m potato.codebook_cli config.yaml --dry-runAPI
All routes sit under /api/codebook and need a logged-in session. Admin routes accept an admin or an adjudicator.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/codebook | the tree, the flat label list, the revision, the codebook schemes, and the caller's can_add and can_edit |
| GET | /api/codebook/version | the structural and content revisions, for cheap polling |
| GET | /api/codebook/similar?name= | existing codes with names close to name |
| GET | /api/codebook/provenance?instance_id= | whether the caller's coding of one item predates the current revision, and the codes added since |
| GET | /api/codebook/stale | the caller's Review list |
| POST | /api/codebook | add a code (name, optional color and parent_id) |
| PATCH | /api/codebook/<id> | rename (name), recolor (color), or move (parent_id) |
| DELETE | /api/codebook/<id> | delete a code and every code under the code |
| POST | /api/codebook/proposals | queue a change (op, payload, actor_kind) |
| GET | /api/codebook/admin/stale | the Review list for every coder (admin) |
| POST | /api/codebook/admin/merge | merge src_id into dst_id (admin) |
| POST | /api/codebook/admin/split | move annotator's links from src_id to target_id or a new code named new_name (admin) |
| GET | /api/codebook/admin/changes | the change log (admin) |
| GET | /api/codebook/admin/proposals | pending proposals (admin) |
| POST | /api/codebook/admin/proposals/<id>/confirm | run a proposal (admin) |
| POST | /api/codebook/admin/proposals/<id>/reject | discard a proposal (admin) |
The definition editor uses GET /api/codebook/document, GET and PUT /api/codebook/blocks, POST /api/codebook/parse, GET /api/codebook/history, GET /api/codebook/history/<id>, POST /api/codebook/restore, and GET /api/codebook/distilled. Under QDA Mode, /qda/codebook returns a read-only view of the codebook.
Codebook export and change history
The codebook export writes codebook.csv, one row per code per codebook scheme, with the columns schema, annotation_type, code, parent, description, color, and n_uses. The export reads the codebook database. Codes added during coding are included, and merged codes are left out. description holds the code's short definition, or the full definition when the code has no short definition. n_uses counts saved codings by name. A coding saved under a name the codebook no longer has counts toward no row.
python -m potato.export -c config.yaml --format codebook -o ./export/The quotation_report export lists every coded passage with the passage's code. The qdpx export hands the codes and codings to another QDA program. QDA Mode covers both exports, and Export Formats lists the columns.
The history stays in project.sqlite. The codebook_change table logs every rename, recolor, move, delete, merge, split, and confirmed or rejected proposal, with the old and new values, the actor, whether the actor was a human or a model, and the revision. In 2.13.1, a rename, recolor, move, or delete made through PATCH or DELETE is logged with the actor system, and code additions appear in the codes table with the creator's username instead of in the log. GET /api/codebook/admin/changes returns the change log as JSON.
codebook_snapshot keeps every saved version of every definition, and codebook_revision_history records when each revision happened. A project snapshot and the archive from potato deploy pull both include project.sqlite. Either bundle is an audit trail of the codebook for a methods section.
Run the bundled example from a Potato checkout:
python potato/flask_server.py start examples/advanced/codebook-example/config.yaml -p 8000The example codes five patient comments about clinic access with an open multiselect codebook of four themes. examples/advanced/codebook-document-example/ adds a distiller block. Open /codebook in that example to see the definition editor.
Further reading
- QDA Mode, for the codebook together with memos, cases, and search
- Memos, for analytic notes beside the codes
- Developing a Qualitative Codebook, on writing definitions and revising codes as a team
- Intercoder Reliability in Qualitative Research, on when to report agreement and which coefficient to use
- Agreement Drift, for agreement over time against codebook revisions
References
MacQueen, K. M., McLellan, E., Kay, K., & Milstein, B. (1998). Codebook Development for Team-Based Qualitative Analysis. CAM Journal, 10(2), 31-36. https://doi.org/10.1177/1525822X980100020301
For implementation details, see the source documentation.