Blog › ICP guides
Red developer on retainer: reactive programming, react blocks, Red series, object replacement, and Red/System systems programming on monthly retainer
November 17, 2026 · ~16 min read
A Red program using the reactive programming model was producing four stale values per run after a source object was replaced. The program used Red’s react facility to automatically update a derived value whenever two reactive source objects changed. The formula block was established with react [derived: source1/field1 + source2/field2] and initially evaluated correctly — when either source field was modified in place with a word assignment (source1/field1: new-value), the reactor fired and derived updated correctly. The failure occurred when the program replaced source1 entirely with a new object constructed via make object! [field1: new-value field2: ...]. Red’s reactive system tracks relationships by object reference, not by variable name. The react formula had captured a reference to the original source1 object at the moment the formula was established. When source1 was rebound to point to a new object, the reactive relationship still pointed to the old object — modifications to the new object through source1/field1: no longer triggered the reactor because the reactor was watching the old object, not the variable named source1. Four stale values per run after source replacement. The Red developer on retainer diagnosed the stale reactive reference: restructured the program to use react? to test whether the relationship was still valid after source replacement, call clear-reactions to remove the stale formula, and re-establish a fresh react formula against the new source object. Stale values per run after source replacement: 4 → 0.
The work log entry read “fixed stale reactive values, 15h.” It names the symptom and duration. It cannot explain to a client why Red’s reactive system tracks object references rather than variable names — a react formula accesses fields of objects that exist at specific memory addresses; when the formula is first evaluated, Red records the specific object instances whose fields were read during that evaluation and registers those instances as the formula’s source dependencies; subsequent modifications to those specific instances trigger the formula to re-evaluate; if those instances are replaced with new objects (the variable is rebound to point to different memory), the original instances are still the registered dependencies, and modifications through the new binding do not trigger the formula, because the formula has no knowledge of which variable names point to the objects it watches — only which objects it read during the last evaluation. It cannot explain why the fix required a three-step cycle rather than just calling clear-reactions alone (clearing the reactions removes all reactive relationships established for the formula, but then the formula is no longer reactive; the subsequent react call re-evaluates the formula against the current values of the bindings, capturing references to the new source objects as the new dependencies; without the re-evaluation step, the derived value is correct only at the moment of clearing but will not update when the new sources change again) or why do-react alone was insufficient (do-react forces a re-evaluation of existing reactive formulas using their currently registered dependencies, but does not update the dependency registration to include the new source objects; the new source objects are still not in the dependency graph after do-react; the derived value is updated once but still becomes stale on the next source modification). The 15 hours of reactive relationship tracking analysis, object replacement detection design, and clear-reactions/react re-establishment discipline across all replacement sites are invisible in the diff beyond the added react? check and the clear-reactions/react cycle.
Red reactive programming: react blocks, is-reactive?, react?, clear-reactions, and the object-reference tracking model
Red’s reactive programming system is built on the reactor! and deep-reactor! object types and the react function. A reactor! object is an object whose field assignments automatically notify any reactive formulas that have registered dependencies on those fields. Declaring a reactor: source1: make reactor! [field1: 10 field2: 20]. A reactive formula is established with react: react [derived: source1/field1 + source1/field2]. When the react call evaluates the block, Red tracks which reactor objects and which fields were accessed. Each accessed field on each accessed reactor object is registered as a dependency of the formula. Subsequent assignments to any registered field trigger the formula to re-evaluate. The trigger mechanism: a reactor! field setter calls the reactor notification system, which walks the list of formulas registered as dependents of that field and evaluates each formula. This pull-on-push model means each formula is evaluated at most once per triggering change, regardless of how many of its dependencies changed in the same cycle.
The dependency registration is captured at formula evaluation time. When react [derived: source1/field1 + source1/field2] first runs, Red records: “this formula accessed field1 and field2 on the specific reactor! object currently referenced by source1.” The formula does not record the name source1; it records the object identity (memory address) of the reactor. This object-reference tracking model is the source of the stale-reference hazard: source1 is a variable binding; the formula watches the object the variable pointed to when the formula was first evaluated, not the variable itself. If source1 is later rebound to a new object (source1: make reactor! [field1: 99 field2: 0]), the formula still watches the old object. Assignments through the new binding do not trigger the formula. Assignments to the old object — which is now unreachable through any named variable but still exists as a registered dependency — still trigger the formula with the old values. This is the definition of a stale reactive relationship: the formula’s dependency registration refers to an object that is no longer the intended source.
is-reactive? tests whether a specific object has any reactive formulas registered as dependents. react? tests whether a specific formula is currently active (has registered dependencies). clear-reactions removes all reactive relationships established for a given formula or for a given reactor object. do-react forces a re-evaluation of a formula using its currently registered dependencies. The correct re-establishment cycle after object replacement: (1) call clear-reactions on the formula to remove the stale dependency registration; (2) reassign the variable to the new object (source1: new-object); (3) call react again with the same formula block to re-evaluate and register new dependencies against the new object. The react re-call must happen after the variable reassignment, not before; if the formula block is re-evaluated before the variable points to the new object, Red will register the old object again (if the variable still points to the old object at that moment) or register no reactor dependency (if the variable points to a non-reactor object at that moment). Sequencing matters: clear, reassign, re-react.
deep-reactor! extends the reactor model to nested objects. A deep-reactor! monitors field changes at any depth within its object hierarchy: modifying a field of a nested object that is itself a field of the deep-reactor! triggers the reactor’s dependents. This is valuable for object trees where reactive formulas need to respond to modifications deep in the tree without explicit notification wiring at each nesting level. The hazard: a deep-reactor! with a large nested object tree may trigger reactive formula re-evaluation on every deep modification, including modifications that the formula does not depend on; performance degrades if the formula’s computation cost is high and the object tree is modified frequently at non-formula-relevant depths. The retainer fix in these cases is to restructure the reactive formula to depend on a shallower, more-targeted subset of the object tree, or to replace the monolithic deep-reactor! with a flatter reactor! structure where only the fields the formula cares about are at the top level.
Red series: block!/string!/binary!, positional navigation, series modification, and the copy discipline
Red’s series are the language’s primary data structure abstraction. A series is a sequence of values with a current position. The series types include block! (heterogeneous sequence of any Red values), paren! (block evaluated differently by the interpreter), string! (Unicode character sequence), binary! (byte sequence), file! (file path series), url! (URL series), path! (path notation series), refinement! (word with leading slash), and tag! (HTML/XML tag series). All series types share the same positional navigation and modification functions: head returns a reference at the beginning of the series; tail returns a reference at one past the end; next advances one position; back retreats one position; at returns a reference at a specific index; skip advances by n positions. Navigation functions return new series references at the new position; the original reference is unchanged.
Series modification functions operate at the current position of the series reference: append adds values at the tail; insert inserts values at the current position; remove removes values at the current position; change replaces values at the current position. All modification functions operate in-place on the series’s underlying storage. This in-place modification model means that two series references pointing to the same underlying series storage will both see modifications made through either reference. This is Red’s series aliasing behavior: when a series is assigned to a new word without copy, both words reference the same underlying storage; modifying the series through one word modifies it for both. The copy function creates a new series with independent storage: b2: copy b1 creates a new block with the same values but independent storage; modifying b2 does not affect b1. copy/part creates a new series containing a specified number of values from the current position.
The copy discipline in Red is analogous to the deep-copy discipline in other languages: it is required whenever a function receives a series argument that it must not modify, or whenever two parts of a program must maintain independent copies of a series that starts with the same content. A common retainer pattern is auditing all series assignments in a codebase to determine which assignments create aliases (no copy) and which create independent copies (with copy), then verifying that the aliasing is intentional at each assignment site. Unintentional aliasing produces bugs where modifying a series in one context unexpectedly modifies what appears to be an independent series in another context. The diagnostic: pick a series variable that is producing wrong values; trace back through all assignment sites where that variable was last written; identify which assignment created an alias to a shared series versus a fresh copy; insert copy at the aliased assignment if the programmer intended independent storage.
Red’s parse dialect provides a powerful pattern-matching engine built on the series model. A parse rule is a block of rules that describe the expected structure of the input series; the parse function attempts to match the rules against the series, returning true if the entire series matches or false otherwise. parse rules can use literals (match exact values), words (match by rule name), some/any/opt (quantifiers), into (recurse into a sub-series), keep (capture matched values), and change/remove/insert (modify the input series during parsing). The parse dialect is Red’s primary tool for writing recursive-descent parsers, configuration file readers, DSL interpreters, and data transformation pipelines. Retainer work on Red parse rules typically involves debugging rules where a quantifier or alternation interacts unexpectedly with series position advancement — a rule that advances the series position on a partial match may leave the parser in an unexpected position for the next rule application.
Red/System integration, view dialect GUI, actors, and Nenad Rakocevic’s Red language design
Red/System is Red’s systems programming sublanguage, a C-level language embedded within the Red toolchain. Red/System code is compiled ahead-of-time to native machine code and provides direct access to C-compatible types, struct declarations, pointer arithmetic, and FFI calls to native libraries. A Red/System function is declared with the func keyword and a Red/System type signature; it can be called from Red code through the FFI bridge. The bridge declaration uses import to declare the Red/System function signatures visible to the Red compiler. Red/System struct declarations must match the memory layout expected by the C library being interfaced; field ordering, alignment, and padding must be correct for the target platform. Struct alignment bugs — where the Red/System struct declaration has wrong padding between fields relative to the C compiler’s ABI — produce wrong values when the struct is passed to or received from a C function, typically manifesting as fields shifted by a few bytes, producing values that are plausibly wrong rather than obviously garbage.
Red’s view dialect provides a declarative GUI toolkit for building native-looking graphical interfaces. A view call takes a layout block describing the GUI structure: faces (widgets), their properties, and their arrangement. The view dialect is built on the face! object model, where each widget is represented as a face! object with properties (size, offset, color, text, enabled, visible) and actors (event handler blocks). Actors are named blocks within a face! that are called when specific events occur: on-click is called when the widget is clicked; on-change is called when the widget’s value changes; on-time is called periodically by the event loop. Reactive formulas integrate with the view dialect through face field bindings: a react formula can reference face/text or face/data as a source, causing the formula to re-evaluate whenever the face’s field changes, enabling automatic UI updates from reactive data sources. The retainer pattern for reactive GUI debugging is similar to the reactive data debugging pattern: verify that the face objects being tracked by reactive formulas are the same objects as the current face instances (the view dialect may create new face objects when the layout is re-rendered), and re-establish reactive relationships after any layout refresh.
Nenad Rakocevic began designing Red in 2010 as a successor to Carl Sassenrath’s Rebol, addressing several Rebol limitations: Rebol was interpreted only (no native compilation), lacked a systems programming layer (no FFI for C libraries without external bindings), and had a closed-source toolchain. Red’s design goals: full-stack language (Red/System for systems-level code; Red for application-level code; the same toolchain compiles both), self-hosted (Red’s compiler is written in Red/System, bootstrapped from a precompiled binary), and Rebol-compatible (Red inherits Rebol’s series model, block-based syntax, and dialecting philosophy). Red’s dialecting philosophy is that the block-based syntax — where any block is data until explicitly interpreted by a function — enables domain-specific languages to be embedded naturally in Red source: the parse dialect, the view dialect, the draw dialect, Red/System, and user-defined DSLs all use the same syntactic form. Red’s closest conceptual relatives are Rebol (series model, block syntax, dialecting) and Forth (stack orientation, minimalist core), but Red’s native compilation and Red/System integration distinguish it from both.
The draw dialect is Red’s 2D graphics DSL, embedded in face! objects to provide custom rendering. A draw block contains rendering commands: line, box, circle, polygon, text, image, fill-pen, line-width, and others. Draw blocks are evaluated by the Red graphics backend (backed by platform-native 2D rendering: GDI+ on Windows, Cairo on Linux, Quartz on macOS). Reactive formulas can update draw blocks in response to data changes, producing animated or data-driven graphics. Retainer work on draw dialect integrations typically involves debugging coordinate calculations (draw coordinate space origins and axis directions vary by platform and context), pen and fill state leakage (a fill-pen set in one draw block affects all subsequent drawing until explicitly reset; a developer who did not reset the fill pen at the end of a block may get wrong colors in subsequent draw commands in the same face), and image caching (images loaded into draw blocks are cached by path; changing a file on disk does not invalidate the cache without an explicit cache-clear operation).
How HourTab tracks Red developer retainer hours
Red retainer work shares the invisible-work problem common to all reactive programming retainers, compounded by Red’s silent stale-reference behavior — a reactive formula that depends on a replaced object does not raise an error; it simply stops updating, producing values that are plausibly correct (the last correctly-computed value) rather than obviously wrong. A stale reactive relationship fix is a diff with three lines: a react? check, a clear-reactions call, and a new react call; the value is elimination of all stale values caused by reactive formulas tracking replaced objects, a correct understanding of Red’s object-reference tracking model, and a react?/clear-reactions/react discipline applied at every object replacement site in the codebase to prevent the same stale-reference pattern from reappearing when new replacement sites are added. A series aliasing fix is a diff with one added copy call; the value is correct series independence at the assignment site, elimination of all unexpected series mutations in the context that depended on independent storage, and a series ownership discipline that distinguishes aliased (shared) from copied (independent) series throughout the module. A Red/System struct alignment fix is a diff with alignment annotations or reordered field declarations; the value is correct memory layout for the C FFI call, elimination of all wrong-field-value reads from the C struct, and a struct declaration that explicitly documents the target platform’s expected alignment.
HourTab gives Red developers a public retainer-hours URL they send to clients — typically organizations building cross-platform desktop applications with Red’s view dialect, research teams using Red for language experiment platforms, and groups building systems tools with the Red/System FFI — at the start of an engagement. For Red retainers, each work log entry should name the mechanism (react block formula design; is-reactive?/react?/clear-reactions discipline; do-react triggering; reactive relationship re-establishment after object replacement; reactor!/deep-reactor! patterns; Red series positional navigation; series modification with append/insert/remove; copy/part discipline; series sharing and aliasing analysis; Red/System C-level FFI; declare/import bridge; struct alignment; view dialect face! model; actors pattern; draw dialect rendering), the specific objects, react formulas, and series involved in the bug, and the before/after metric. Red retainers are often compared to Rebol developer retainers for the shared series model and block-syntax philosophy and to Erlang developer retainers for reactive message-driven programming patterns. HourTab’s work log makes the reactive relationship tracking analysis, object replacement detection design, and clear-reactions/react re-establishment discipline visible to clients who would otherwise see only the symptom — four stale values per run after source replacement — and not understand why the fix required understanding Red’s object-reference tracking model, diagnosing which object replacements broke the reactive graph, and inserting a three-step react?/clear-reactions/react cycle at each replacement site.
Track Red developer retainer hours without the status emails
HourTab gives Red developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your work log becomes the proof of value that gets the retainer renewed.
See HourTab pricing →FAQ: Red developer retainers
What does a Red developer on retainer typically do?
A Red developer on monthly retainer covers four principal service areas: reactive programming design (react block formulas; is-reactive?/react?/clear-reactions discipline; do-react triggering; reactive relationship re-establishment after object replacement; reactor!/deep-reactor! patterns); Red series programming (block!/string!/binary! series operations; series positional navigation with head/tail/next/back/at/skip; series modification with append/insert/remove/change; copy/part discipline; series sharing and aliasing analysis); Red/System integration (C-level FFI; declare/import bridge; struct type declarations; pointer arithmetic; memory management); and view dialect GUI and actors (view/layout dialect; face! object model; on-click/on-change/on-time actors; reactive GUI bindings; draw dialect 2D graphics).
What Red work is most commonly underlogged in a retainer?
Reactive relationship re-establishment after object replacement (react formula tracked reactive source; source replaced with make object!; react tracked old reference; 4 stale values per run; added react? check and clear-reactions then re-react; stale values: 4/run → 0; 14–22 hrs invisible in reactive relationship tracking analysis, object replacement detection design, and re-establishment discipline); series ownership analysis (series shared between two contexts via same block reference; modification through one context mutated both; copy discipline required to isolate; 8–14 hrs invisible in series aliasing analysis); and Red/System struct alignment bugs (struct with mixed types; alignment padding wrong for target platform; FFI call passed misaligned values; 6–10 hrs invisible in struct alignment analysis).
What are typical Red developer retainer rates?
Entry-level Red developers (1–2 years, basic Red series programming, block! operations, simple react formulas) bill at $65–$110/hr. Mid-level Red engineers (2–4 years, reactive programming with clear-reactions/re-establishment discipline, series ownership analysis, view dialect GUI, Red/System FFI design) bill at $105–$185/hr. Senior Red architects (4–8 years, full Red toolchain, Red/System systems programming, Red compiler contribution, complex reactive system architecture, parse dialect DSL design) bill at $155–$275/hr. Monthly retainer ranges: $1,700–$4,500/mo advisory (15–25 hrs), $6,000–$16,000/mo for full Red system development engagements.
What should a Red developer retainer agreement include?
A Red developer retainer agreement should specify: reactive programming scope (react block formula design; is-reactive?/react?/clear-reactions discipline; do-react triggering; reactive relationship re-establishment after object replacement; reactor!/deep-reactor! patterns); Red series scope (block!/string!/binary! series operations; positional navigation; modification; copy/part discipline; series sharing analysis); Red/System scope (C-level FFI; declare/import bridge; alias/struct type declarations; pointer arithmetic; memory management); view dialect scope (view/layout dialect; face! object model; actors pattern; draw dialect); and hour logging format (advisory category, before/after stale-value metric, Red version, whether fix required clear-reactions/react cycle addition, copy/part insertion, struct alignment correction, or actor binding repair).
How should Red developer retainer hours be logged?
Log each Red retainer session with: advisory category (react block formula design; is-reactive?/react?/clear-reactions discipline; do-react triggering; reactive relationship re-establishment after object replacement; reactor!/deep-reactor! patterns; Red series positional navigation; series modification with append/insert/remove; copy/part discipline; series sharing and aliasing analysis; Red/System C-level FFI; declare/import bridge; struct alignment; view dialect face! model; actors pattern; draw dialect rendering); the specific objects, react formulas, and series involved in the bug (react formula tracking two reactive source objects; one source replaced with make object!; react tracked old reference; 4 stale values per run; added react? check and clear-reactions then re-react on replacement; stale values: 4/run → 0); and the before/after observable metric. Include Red version and whether fix required clear-reactions/react cycle addition, copy/part insertion, struct alignment correction, or actor binding repair.