| name | JBCT |
| description | Java Backend Coding Technology skill for designing, implementing, and reviewing functional Java backend code. Use when working with Result, Option, Promise types, value objects, use cases, or when asked about JBCT patterns, monadic composition, parse-don't-validate, structural patterns (Leaf, Sequencer, Fork-Join), or functional Java backend architecture. |
Java Backend Coding Technology (JBCT)
A methodology for writing predictable, testable Java backend code optimized for human-AI collaboration.
When to Use This Skill
Activate this skill when:
- Learning JBCT principles and patterns
- Quick reference for API usage and examples
- Understanding patterns and when to use them
- Working with
Result<T>, Option<T>, Promise<T> types
- Questions about monadic composition, error handling, or validation patterns
For implementation work: Use jbct-coder subagent (Task tool with subagent_type: "jbct-coder")
For code review: Use jbct-reviewer subagent (Task tool with subagent_type: "jbct-reviewer")
For automated checking: Use jbct CLI tool (format, lint, check commands)
Source-Anchored Chapters (read this first)
Parts of this skill live in the class-level header comments of Pragmatica Core source files —
the single source of truth that cannot drift from the API. When a task touches one of these
areas, READ THE SOURCE HEADER before writing code:
| Chapter | Source of truth |
|---|
| Core monads: combinator maps, construction, aggregation, conversions | org/pragmatica/lang/Result.java, Option.java, Promise.java headers |
Validation: ensure families, full Is predicate catalog, ensureOption, combine | org/pragmatica/lang/Verify.java header |
| Intent annotations: when void/blocking/null is legitimate, decision procedures | org/pragmatica/lang/Contract.java, TerminalOperation.java, NullReturn.java headers |
| Built-in value objects: catalog, factories, validation rules | org/pragmatica/lang/vo/package-info.java (+ per-class headers) |
| Exception-safe parsing: wrapper catalog | org/pragmatica/lang/parse/package-info.java |
| Utilities: failure vocabulary, resilience (Retry/CircuitBreaker/RateLimiter/Idempotency), memoization, scheduling | org/pragmatica/lang/utils/package-info.java |
Resolution order (first that succeeds):
0. jbct doc <ClassOrPackage> — if the jbct CLI is available (jbct --version), this is the
preferred shortcut: jbct doc Verify, jbct doc org.pragmatica.lang.vo, jbct doc Result --api.
It applies the local-then-download resolution below automatically.
- Local checkout (preferred if present) — if a local pragmatica checkout exists, read
<checkout>/core/src/main/java/<path> directly. In sibling layouts (e.g. working inside
coding-technology/) this is ../pragmatica/core/src/main/java/<path>; inside the pragmatica
repo itself it is core/src/main/java/<path>.
- Download if no local checkout — fetch the Pragmatica Core sources for the project's declared
core version, then read the header:
- Maven Central sources jar:
mvn dependency:get -Dartifact=org.pragmatica-lite:core:<version>:jar:sources then
unzip -p ~/.m2/repository/org/pragmatica-lite/core/<version>/core-<version>-sources.jar org/pragmatica/lang/Verify.java | head -120
- or GitHub release / raw:
https://raw.githubusercontent.com/pragmaticalabs/pragmatica/main/core/src/main/java/<path>
- Degrade gracefully — proceed with this file's summaries plus imitation of neighboring code,
and say in your report that source chapters were unreachable.
Read only the class header (first ~120 lines), not whole implementation files.
JBCT CLI Tool
JBCT CLI provides automated formatting and compliance checking with 37 lint rules.
Check if installed:
jbct --version
Usage:
jbct format src/main/java
jbct lint src/main/java
jbct check src/main/java
jbct init --slice my-service
jbct add-slice <name>
jbct add-event <name>
jbct add-persistence
If not installed, suggest:
💡 JBCT CLI automates formatting and 37 lint rules for JBCT compliance.
Install: curl -fsSL https://raw.githubusercontent.com/siy/jbct-cli/main/install.sh | sh
Requires: Java 25+
More info: https://github.com/siy/jbct-cli
Core Philosophy
JBCT reduces the space of valid choices to one good way to do most things through:
- Four Return Kinds: Every function returns exactly one of
T, Option<T>, Result<T>, Promise<T>
- Parse, Don't Validate: Make invalid states unrepresentable
- No Business Exceptions: Business failures are typed
Cause values
- Thread Safety by Design: Immutability at boundaries, thread confinement for sequential logic
- Six Structural Patterns: All code fits one pattern (Leaf, Sequencer, Fork-Join, Condition, Iteration, Aspects)
FORBIDDEN PATTERNS (Zero Tolerance)
These patterns are never acceptable in JBCT code. Hunt for them aggressively.
🔴 CRITICAL VIOLATIONS
| Violation | Detection | Why Forbidden |
|---|
*Impl classes | grep -r "class.*Impl" | Use lambdas for behavior, records for data |
| Null checks in business logic | if (x == null) or != null | Use Option<T> instead |
| Throwing exceptions | throw new in business code | Use Result<T> or Promise<T> |
| Catching exceptions | catch in business code | Lift at adapter boundaries only |
Void type parameter | Result<Void>, Promise<Void> | Use Unit. void return OK with @Contract (external API) or fire-and-forget |
Result.failure(cause) | Direct call | Use cause.result() fluent style |
Promise.failure(cause) | Direct call | Use cause.promise() fluent style |
| Multi-statement lambdas | x -> { stmt1; stmt2; } | Extract to named method |
Promise.await() in business logic | .await() in domain/usecase | Blocks thread. Stay in chain. OK in tests; use @TerminalOperation for CLI/fire-and-forget |
@SuppressWarnings misuse | Used instead of @Contract/@TerminalOperation | Use @Contract for void return (external API), @TerminalOperation for legitimate await() |
Abandoned Result/Promise | Statement discarding return value | Every Result/Promise must be returned or handled |
Exception: Methods annotated with @Contract are exempt from all JBCT lint rules. Use @Contract for Java API boundary methods (annotation processors, Maven Mojos).
⚠️ WARNING PATTERNS
| Pattern | Issue | Fix |
|---|
fold() for simple cases | Obscures intent | Use .toResult(), .async(), .or() |
| Complex lambda body | Logic in map/flatMap | Extract to method reference |
| Long sequencer chains | >5 flatMap calls | Group into sub-operations |
| Nested records for behavior | record X() implements Y {} | Use lambda |
Examples
public class UserServiceImpl implements UserService { ... }
static UserService userService(UserRepository repo) {
return userId -> repo.findById(userId);
}
if (user != null) { process(user); }
findUser(id).onSuccess(this::process);
return Result.failure(USER_NOT_FOUND);
return USER_NOT_FOUND.result();
.map(user -> {
var enriched = enrich(user);
return format(enriched);
})
.map(this::enrichAndFormat)
Quick Reference
The Four Return Kinds
public String initials() { return ...; }
public Option<Theme> findTheme(UserId id) { return ...; }
public static Result<Email> email(String raw) { return ...; }
public Promise<User> loadUser(UserId id) { return ...; }
Critical Rules:
- ❌ Never
Promise<Result<T>> - Promise already handles failures
- ❌ Never
Void type parameter - always use Unit (Result<Unit>, Promise<Unit>). void return is OK for fire-and-forget
- ✅ Use
Result.unitResult() for successful Result<Unit>
Parse, Don't Validate Pattern
public record Email(String value) {
private static final Fn1<Cause, String> INVALID_EMAIL =
Causes.forOneValue("Invalid email: %s");
public static Result<Email> email(String raw) {
return Verify.ensure(raw, Verify.Is::present)
.map(String::trim)
.filter(INVALID_EMAIL, PATTERN.asMatchPredicate())
.map(Email::new);
}
}
public record Email(String value) {
public Result<Email> validate() { ... }
}
Key Points:
- Factory method named after type (lowercase):
Email.email(...)
- Constructor private or package-private
- If instance exists, it's valid
Pragmatica Core Validation Utilities
Verify first, lambdas last. Before writing any predicate lambda in a validation chain, check
the Verify.Is catalog — full catalog and ensure overload families: see the Verify.java
source header (Source-Anchored Chapters above). Hand-rolling a check that duplicates a catalog
predicate is a JBCT violation. Hottest entries:
Verify.Is::present
Verify.ensure(v, Is::lenBetween, 3, 50)
Verify.ensureOption(opt, predicate)
Parse Subpackage - Exception-safe JDK wrappers:
import org.pragmatica.lang.parse.Number;
import org.pragmatica.lang.parse.DateTime;
import org.pragmatica.lang.parse.Network;
Number.parseInt(raw)
DateTime.parseLocalDate(raw)
Network.parseUUID(raw)
Example:
public record Age(int value) {
private static final Cause AGE_OUT_OF_RANGE = Causes.cause("Age must be 0-150");
public static Result<Age> age(String raw) {
return Number.parseInt(raw)
.filter(AGE_OUT_OF_RANGE, v -> Verify.Is.between(v, 0, 150))
.map(Age::new);
}
}
Use Case Structure
public interface RegisterUser extends UseCase.WithPromise<Response, Request> {
record Request(String email, String password) {}
record Response(UserId userId, ConfirmationToken token) {}
interface CheckEmail { Promise<ValidRequest> apply(ValidRequest valid); }
interface SaveUser { Promise<User> apply(ValidRequest valid); }
record ValidRequest(Email email, Password password) {
static Result<ValidRequest> validRequest(Request raw) {
return Result.all(Email.email(raw.email()),
Password.password(raw.password()))
.map(ValidRequest::new);
}
}
static RegisterUser registerUser(CheckEmail checkEmail, SaveUser saveUser) {
return request -> ValidRequest.validRequest(request)
.async()
.flatMap(checkEmail::apply)
.flatMap(saveUser::apply);
}
}
❌ ANTI-PATTERN: Nested Record Implementation
NEVER create factories with nested record implementations:
static RegisterUser registerUser(CheckEmail check, SaveUser save) {
record registerUser(CheckEmail check, SaveUser save) implements RegisterUser {
@Override
public Promise<Response> execute(Request request) { ... }
}
return new registerUser(check, save);
}
Rule: Records are for data (value objects), lambdas are for behavior (use cases, steps).
Thread Safety Essentials
Core Rules:
- Immutable at boundaries: All shared data (parameters, return values) must be immutable
- Thread confinement: Mutable state allowed within single-threaded execution (sequential patterns)
- Fork-Join requires immutability: Parallel operations must not share mutable state
Pattern-Specific Safety:
- Leaf, Sequencer, Condition, Iteration: Thread-safe through sequential execution. Mutable local state OK.
- Fork-Join: Requires strict immutability. All parallel operations receive immutable inputs.
- Promise resolution: Thread-safe (exactly-once semantics, synchronization point for flatMap/map chains)
Example - Thread-Safe Fork-Join:
Promise.all(applyBogo(cart),
applyPercentOff(cart))
.map(this::mergeDiscounts);
private final DiscountContext context = new DiscountContext();
Promise.all(applyBogo(cart, context),
applyPercentOff(cart, context))
.map(this::merge);
See ch09-thread-safety.md for comprehensive thread safety coverage, including detailed examples and common mistakes.
Lambda Composition Guidelines
Rule: Lambdas passed to monadic operations (map, flatMap, recover, filter) must be minimal.
Allowed:
- Method references:
Email::new, this::processUser, User::id
- Parameter forwarding:
user -> validate(requiredRole, user)
- Constructor references for error mapping:
RepositoryError.DatabaseFailure::new
Forbidden:
- Conditionals (
if, ternary, switch)
- Try-catch blocks
- Multi-statement blocks
- Object construction beyond simple factory calls
Pattern matching: Use switch expressions in named methods:
.recover(this::recoverKnownErrors)
private Promise<T> recoverKnownErrors(Cause cause) {
return switch (cause) {
case NotFound ignored, Timeout ignored -> DEFAULT.promise();
default -> cause.promise();
};
}
Multi-case matching: Comma-separated for same recovery:
private Promise<Theme> recoverWithDefault(Cause cause) {
return switch (cause) {
case NotFound ignored, Timeout ignored, ServiceUnavailable ignored ->
Promise.success(Theme.DEFAULT);
default -> cause.promise();
};
}
Error constants: Define once, reuse everywhere:
Pattern Decomposition & Data Flow
Mandatory: Maximum Decomposition
Rule: One pattern per method. Never combine patterns in a single method body.
public Promise<Response> execute(Request request) {
return validate(request)
.async()
.flatMap(valid -> {
if (valid.isPremium()) {
return Promise.all(fetchA(valid), fetchB(valid))
.map(this::merge);
}
return fetchBasic(valid);
});
}
public Promise<Response> execute(Request request) {
return validate(request)
.async()
.flatMap(this::routeByType);
}
private Promise<Response> routeByType(ValidRequest valid) {
return valid.isPremium()
? processPremium(valid)
: processBasic(valid);
}
private Promise<Response> processPremium(ValidRequest valid) {
return Promise.all(fetchA(valid), fetchB(valid))
.map(this::merge);
}
Data Flow: Track Dependencies Explicitly
Every method must have clear data flow:
- Input: What data does it need?
- Output: What data does it produce?
- Dependencies: What external services/steps does it call?
private Promise<User> createUser(ValidRequest valid) {
return hashPassword.apply(valid.password())
.flatMap(hashed -> userRepository.save(
new User(UserId.generate(), valid.email(), hashed)));
}
Growing Context Pattern
When multi-step operations need data from earlier steps, use explicit intermediate records instead of nested closures:
return loadUser(userId)
.flatMap(user -> loadOrders(user.id())
.flatMap(orders -> loadPreferences(user.id())
.map(prefs -> new Dashboard(user, orders, prefs))));
record UserWithOrders(User user, List<Order> orders) {}
record DashboardContext(User user, List<Order> orders, Preferences prefs) {}
return loadUser(userId)
.flatMap(user -> loadOrders(user.id())
.map(orders -> new UserWithOrders(user, orders)))
.flatMap(ctx -> loadPreferences(ctx.user().id())
.map(prefs -> new DashboardContext(ctx.user(), ctx.orders(), prefs)))
.map(this::buildDashboard);
Benefits:
- Each stage has clear input/output types
- No deeply nested closures
- Easy to add/remove stages
- Debuggable intermediate states
private static final Cause NOT_FOUND = new UserNotFound("User not found");
private static final Cause TIMEOUT = new ServiceUnavailable("Request timed out");
private Promise<User> recoverNetworkError(Cause cause) {
return switch (cause) {
case NetworkError.Timeout ignored -> TIMEOUT.promise();
default -> cause.promise();
};
}
Structural Patterns
JBCT's six patterns come from the process side — the data dependency graph's operators — and code written in them is an executable business process specification.
| Pattern | Role |
|---|
| Leaf | Atomic operation, one responsibility |
| Sequencer | Dependent steps in order |
| Fork-Join | Independent concurrent operations |
| Condition | Routing, no transformation |
| Iteration | Collection processing |
| Aspects | Cross-cutting concerns wrapping logic |
1. Leaf Pattern
Atomic unit - single responsibility, no composition:
public Promise<User> findUser(UserId id) {
return Promise.lift(
RepositoryError.DatabaseFailure::new,
() -> jdbcTemplate.queryForObject(...)
);
}
2. Sequencer Pattern
Linear dependent steps (most common use case pattern):
return ValidRequest.validRequest(request)
.async()
.flatMap(checkEmail::apply)
.flatMap(hashPassword::apply)
.flatMap(saveUser::apply)
.flatMap(sendEmail::apply);
3. Fork-Join Pattern
Parallel independent operations (requires immutable inputs):
return Promise.all(fetchProfile.apply(userId),
fetchPreferences.apply(userId),
fetchOrders.apply(userId))
.map((profile, prefs, orders) ->
new Dashboard(profile, prefs, orders));
Thread Safety: All parallel operations must receive immutable inputs. No shared mutable state.
4. Condition Pattern
Branching as values (no mutation):
return userType.equals("premium")
? processPremium.apply(request)
: processBasic.apply(request);
5. Iteration Pattern
Functional collection processing:
var results = items.stream()
.map(Item::validate)
.toList();
return Result.allOf(results)
.map(validItems -> process(validItems));
6. Aspects Pattern
Cross-cutting concerns without mixing:
return withRetry(
retryPolicy,
withMetrics(metricsPolicy, coreOperation)
);
Type Conversions
option.toResult(cause)
option.async(cause)
result.async()
promise.await()
promise.await(timeout)
cause.result()
cause.promise()
Aggregation Operations
Result.all(result1, result2, result3)
.map((v1, v2, v3) -> combine(v1, v2, v3));
Promise.all(promise1, promise2, promise3)
.map((v1, v2, v3) -> combine(v1, v2, v3));
Promise.allOrCancel(promise1, promise2, promise3)
.map((v1, v2, v3) -> combine(v1, v2, v3));
Option.all(opt1, opt2, opt3)
.map((v1, v2, v3) -> combine(v1, v2, v3));
Promise.allOf(collection)
Promise.allOfOrCancel(collection)
Promise.any(promise1, promise2)
Result.allOf(collection)
Instance variants (for-comprehension style, same semantics):
promise.all(fn1, fn2, fn3).map(combine);
promise.allOrCancel(fn1, fn2, fn3).map(combine);
Exception Handling
Promise.lift(
RepositoryError.DatabaseFailure::new,
() -> jdbcTemplate.queryForObject(...)
);
Result.lift(
CustomError.ProcessingFailed::new,
() -> riskyOperation()
);
Naming Conventions
- Factory methods:
TypeName.typeName(...) (lowercase-first)
- Validated inputs:
Valid prefix (not Validated): ValidRequest, ValidUser
- Error types: Past tense verbs:
EmailNotFound, AccountLocked, PaymentFailed
- Test names:
methodName_outcome_condition
- Acronyms: Treat as words (camelCase):
httpClient, apiKey not HTTPClient, APIKey
Zone-Based Naming (Abstraction Levels)
Source: Adapted from Derrick Brandt's systematic approach.
Use zone-appropriate verbs to maintain consistent abstraction levels:
Zone 2 (Step Interfaces - Orchestration):
- Verbs:
validate, process, handle, transform, apply, check, load, save, manage, configure, initialize
- Examples:
ValidateInput, ProcessPayment, HandleRefund, LoadUserData
Zone 3 (Leaves - Implementation):
- Verbs:
get, set, fetch, parse, calculate, convert, hash, format, encode, decode, extract, split, join, log, send, receive, read, write, add, remove
- Examples:
hashPassword(), parseJson(), fetchFromDatabase(), calculateTax()
Anti-pattern: Mixing zones (e.g., step interface named FetchUserData uses Zone 3 verb fetch instead of Zone 2 verb load)
Stepdown rule test: Read code aloud with "to" before functions - should flow naturally:
return ValidRequest.validRequest(request)
.async()
.flatMap(this::processPayment)
.flatMap(this::sendConfirmation);
For complete zone verb vocabulary, see ch07-basic-patterns.md.
Project Structure (Vertical Slicing)
com.example.app/
├── usecase/
│ ├── registeruser/ # Self-contained vertical slice
│ │ ├── RegisterUser.java # Use case interface + factory
│ │ └── [internal types] # ValidRequest, etc.
│ └── loginuser/
│ └── LoginUser.java
├── domain/
│ └── shared/ # Reusable value objects ONLY
│ ├── Email.java
│ ├── Password.java
│ └── UserId.java
└── adapter/
├── rest/ # Inbound (HTTP)
├── persistence/ # Outbound (DB)
└── messaging/ # Outbound (queues)
Placement Rules:
- Value objects used by single use case → inside use case package
- Value objects used by 2+ use cases →
domain/shared/
- Steps (interfaces) → always inside use case
- Errors → sealed interface inside use case
Error Structure (General enum pattern):
public sealed interface RegistrationError extends Cause {
enum General implements RegistrationError {
EMAIL_ALREADY_REGISTERED("Email already registered"),
WEAK_PASSWORD_FOR_PREMIUM("Premium codes require 10+ char passwords");
private final String message;
General(String message) { this.message = message; }
@Override public String message() { return message; }
}
record PasswordHashingFailed(Throwable cause) implements RegistrationError {
@Override public String message() { return "Password hashing failed"; }
}
}
RegistrationError.General.EMAIL_ALREADY_REGISTERED.promise()
Testing Patterns
@Test
void validation_fails_forInvalidInput() {
ValidRequest.validRequest(new Request("invalid", "bad"))
.onSuccess(Assertions::fail);
}
@Test
void validation_succeeds_forValidInput() {
ValidRequest.validRequest(new Request("valid@example.com", "Valid1234"))
.onFailure(Assertions::fail)
.onSuccess(valid -> {
assertEquals("valid@example.com", valid.email().value());
});
}
@Test
void execute_succeeds_forValidInput() {
useCase.execute(request)
.await()
.onFailure(Assertions::fail)
.onSuccess(response -> {
assertEquals("expected", response.value());
});
}
Pragmatica Core Library
JBCT uses Pragmatica Core 1.0.0-rc1 for functional types.
Maven (preferred):
<dependency>
<groupId>org.pragmatica-lite</groupId>
<artifactId>core</artifactId>
<version>1.0.0-rc1</version>
</dependency>
Gradle (only if explicitly requested):
implementation 'org.pragmatica-lite:core:1.0.0-rc1'
Library documentation: https://central.sonatype.com/artifact/org.pragmatica-lite/core
Library Value Objects
Check org.pragmatica.lang.vo BEFORE writing any value object — hand-rolling a VO that
duplicates a built-in (Email, Url, Uuid, NonBlankString, IsoDateTime) is a JBCT
violation. Catalog with factories and validation rules: see the vo/package-info.java source
header (Source-Anchored Chapters above). Build custom VOs only for domain-specific types
(OrderId, Username, ReferralCode).
Note: Email appears throughout this skill as a teaching example for writing validation
chains — in production code, use org.pragmatica.lang.vo.Email.
Static Imports (Encouraged)
Static imports reduce code verbosity:
import static org.pragmatica.lang.Result.all;
import static org.pragmatica.lang.Result.success;
import static org.pragmatica.lang.vo.Email.email;
import static com.example.domain.Password.password;
return all(email(raw), password(raw)).flatMap(ValidRequest::validRequest);
Fluent Failure Creation
Use cause.result() and cause.promise() instead of Result.failure(cause):
return INVALID_EMAIL.result();
return USER_NOT_FOUND.promise();
return Result.failure(INVALID_EMAIL);
return Promise.failure(USER_NOT_FOUND);
When to Use Specialized Subagents
This skill provides quick reference and learning resources. For complex implementation and review tasks, use specialized subagents:
Use jbct-coder Subagent When:
- Generating complete use case implementations with all components
- Creating value objects with validation and error types
- Implementing adapters with proper exception handling
- Writing tests following JBCT patterns
- Need deterministic code generation following all JBCT rules
How to invoke: Use Task tool with subagent_type: "jbct-coder"
What it provides:
- Complete use case structure (interface, factory, steps)
- Validated request types with
Result.all()
- Value objects with parse-don't-validate pattern
- Error types as sealed interfaces
- Comprehensive test suites (validation, happy path, failures)
- Step-by-step code generation with explanations
Use jbct-reviewer Subagent When:
- Reviewing existing code for JBCT compliance
- Validating patterns (Leaf, Sequencer, Fork-Join, etc.)
- Checking naming conventions and structure
- Identifying violations with specific fixes
- Need comprehensive checklist-based analysis
How to invoke: Use Task tool with subagent_type: "jbct-reviewer"
What it provides:
- Four Return Kinds compliance check
- Parse-don't-validate pattern validation
- Null policy enforcement
- Pattern recognition and verification
- Naming convention compliance
- Detailed violation reports with corrections
Use This Skill When:
- Learning JBCT principles and patterns
- Looking up API usage examples
- Quick reference for type conversions
- Understanding when to use which pattern
- Exploring patterns with examples
Implementation Workflow
- Define use case interface with Request, Response, and execute signature
- Create validated request with static factory using
Result.all()
- Define steps as single-method interfaces (nested in use case)
- Create value objects with validation in static factories
- Implement factory method returning lambda with composition chain
- Write tests starting with validation, then happy path, then failure cases
💡 Tip: For automatic generation following this workflow, use the jbct-coder subagent.
Common Mistakes to Avoid
❌ Using business exceptions instead of Result/Promise
❌ Nested records in use case factories (use lambdas)
❌ Void type parameter (use Unit; void return is OK for fire-and-forget)
❌ Promise<Result<T>> (redundant nesting)
❌ Separate validation methods (parse at construction)
❌ Public constructors on value objects
❌ Complex logic in lambdas (extract to methods)
❌ Validated prefix (use Valid)
💡 Tip: For automated code review checking these mistakes, use the jbct-reviewer subagent.
Self-Validation Checkpoint
Before considering JBCT code complete, verify ALL of these:
Zero Tolerance (must pass)
Pattern Compliance
Data Flow
Naming
Structure
Detailed Resources
This skill contains comprehensive guidance organized by topic:
Fundamentals
Patterns
Use Cases
Testing & Organization
Specialized Subagents
- ../../jbct-coder.md - Autonomous code generation agent (invoke with Task tool)
- Generates complete use cases with validation, tests, and adapters
- Follows deterministic algorithms for consistent output
- Includes evolutionary testing strategy
- ../../jbct-reviewer.md - Autonomous code review agent (invoke with Task tool)
- Comprehensive JBCT compliance checking
- Pattern validation and naming convention enforcement
- Detailed violation reports with fixes
Documentation
- ../../book/index.md - Complete technical reference (JBCT book)
- ../../TECHNOLOGY.md - High-level pattern catalog
- ../../CHANGELOG.md - Version history and changes
Repository: https://github.com/siy/coding-technology