Skip to content
Back to skills

Java Tell Dont Ask

ASecurity

Decision ownership: the type that owns an invariant or policy makes the decision. Use when a service reads state with getters, decides, and writes state back (if (acct.getBalance() > x) acct.setBalance(...)), when the same rule is re-derived from the same getters in several places, when an invariant exists but no type enforces it, when a domain model is all getters and setters with the logic in services, or when a getter has side effects. Covers command–query separation and when asking is cor...

  • 2 stars
  • 0 votes
  • 0 copies
  • 8 views
  • Added September 19, 2026
developmentrustgojavaexpressgitdatabase

Works with

  • cli

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 29, 2026

npx -y skills add robsonkades/agent-skills --skill java-tell-dont-ask --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Java Tell Dont Ask?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Java Tell Dont Ask
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/robsonkades-java-tell-dont-ask/badge)](https://www.skillsdirectory.com/skills/robsonkades-java-tell-dont-ask)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: java-tell-dont-ask
description: >
  Decision ownership: the type that owns an invariant or policy makes the decision. Use when a service
  reads state with getters, decides, and writes state back (if (acct.getBalance() > x)
  acct.setBalance(...)), when the same rule is re-derived from the same getters in several
  places, when an invariant exists but no type enforces it, when a domain model is all
  getters and setters with the logic in services, or when a getter has side effects. Covers
  command–query separation and when asking is correct: boundaries, reporting,
  cross-aggregate orchestration. Does not cover the navigation chains that often carry the
  asking — that is java-law-of-demeter.
---

# Java Tell, Don't Ask

## Purpose

`if (account.getBalance().compareTo(amount) >= 0) account.setBalance(...)` is a rule that
lives nowhere: the invariant "balance never goes below the limit" is enforced only at call
sites that remember to check, and the read–decide–write gap makes it race-prone. This skill
moves such decisions to the type that owns the invariant and has enough authoritative state
to enforce it — and, just as deliberately, refuses to move the ones that do not belong there.
Possessing data is not sufficient ownership: pricing, authorization and cross-aggregate policy
often belong to a policy object or application service. An anemic model over simple CRUD data is a
legitimate architecture; anemia is a problem only when invariants exist and no type owns
them.

## Workflow

0. **Inspect compatibility and lifecycle.** Read compiler release/toolchains, framework/binder
   requirements, entity ownership, transaction/version mapping and public failure contracts.
   Reuse available policy, caller and test evidence; ask only when unresolved ownership or
   eligibility would materially change the fix. Keep independent work moving while that is resolved.
   The domain example uses Java 17; its service pattern switch/record patterns require Java 21+
   without preview. Adapt to the project baseline without upgrading or adding frameworks.
1. **Find ask–decide–mutate sequences**: getters on an object, a branch on the result,
   then a setter or mutation on the same object. Each is a candidate, not a verdict.
2. **Name the invariant and its existing enforcement.** Move a misplaced object-owned guard
   and mutation together into a command; keep an adequate service/transaction owner when that
   is where the rule belongs. Close unsafe mutation paths under the actual public/binder
   compatibility contract, rather than deleting every setter. If there is no misplaced or
   missing rule — the code just shovels data — leave it; a transaction script over data is fine.
3. **Identify the authority and change owner.** A rule spanning aggregates may belong to
   a domain policy, process manager or application service; no participating entity becomes
   the owner merely because it holds one input. Use `references/placement-decision.md`.
4. **Apply an explicit command/query convention.** Strict CQS makes mutating commands
   return `void`; pragmatic command-query separation permits a command to return its own
   outcome. Queries must be observationally side-effect-free. Private, thread-safe memoization
   may preserve that contract; touching externally visible state on read does not.
5. **Verify** the supported mutation/construction paths enforce the owned rule, including
   persistence/binders, mutable aliases retained from inputs or exposed by queries, and concurrency
   boundaries. A side-effect-free getter can still return a mutation bypass. Test the domain
   decision and caller outcome mapping; duplicated enforcement at independent trust boundaries
   may still be required.

## Rules

- Put a decision with the type that owns its invariant or policy and can enforce it from
  authoritative state. Callers express intent; data proximity alone does not establish ownership.
- Public queries expose information that callers can couple policy to. Keep queries needed for
  boundaries, observability and legitimate decisions; close unsafe mutation bypasses and correct
  duplicated external derivations instead of treating every getter as a defect.
- Under strict CQS, commands return `void`. If the codebase adopts the pragmatic variant, a
  command may return its own result or updated representation; document that convention and do
  not mix unrelated answers or externally visible read effects into it.
- A record may be a boundary DTO, a value object or an immutable domain type with behavior.
  Tell-don't-ask applies according to ownership and invariant, not the `record` keyword.
- A method boundary supplies no atomicity: shared in-memory instances need confinement or
  synchronization, and independent persistence contexts need optimistic locking, conditional
  updates or other database guards. State both boundaries; moving a method cannot replace them.
- Keep infrastructure clients and ambient mechanisms out of entities. Pass a validated policy
  input when it is merely data; use a domain policy interface/value object when the behavior has
  its own domain ownership. A long list of fetched inputs is evidence the decision may belong
  outside the entity.
- Asking is correct at boundaries — mappers, serialisation, rendering — in queries and
  reports, and in orchestration across aggregates where no single object can own the rule.

## Deliverable

Name the invariant/policy authority, inputs and freshness, allowed mutation path, caller result
mapping and synchronization/commit boundary. Distinguish a mechanical move from changed failure
or validation behavior. Report checks executed and unresolved ownership evidence; do not infer
a common rule merely because two callers read the same fields. A review may conclude that the
existing owner and queries are adequate: state the evidence and what would justify revisiting it.
When a material policy remains unknown, give the conditional recommendation and the specific
missing decision rather than silently choosing it or prolonging unrelated investigation.

## References

- [Placement decision](references/placement-decision.md) — read when it is unclear whether
  a decision belongs in the object, or the code pattern-matches ask–decide–mutate but is
  correct as it stands, or queries expose mutable state: heuristics, false positives, alias
  protection and the costs of moving.
- [Worked example: withdrawal against a limit](references/worked-example.md) — read before
  moving an invariant-bearing decision out of a service: before, analysis, after, what
  stays in the service, trade-offs, verification.

Files in this skill

  • SKILL.md6.3 KB
  • references/placement-decision.md6.5 KB
  • references/worked-example.md9.2 KB
  • skill.yaml1.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.