Membrane API Gateway — a lightweight Java API gateway for REST, GraphQL, and legacy SOAP/WSDL services, configurable in YAML or XML. Upstream: https://github.com/membrane/api-gateway
Behavioral guidelines to reduce common coding mistakes; they compose with the project-specific conventions below. Tradeoff: these bias toward caution over speed. For trivial tasks, use judgment.
Don't assume silently. Surface tradeoffs.
- State assumptions explicitly rather than picking one silently.
- If multiple reasonable interpretations exist, name them instead of just choosing.
- Ask before proceeding only on judgment calls with real cost to being wrong (data model, auth, hard-to-undo changes). For everything else, state your assumption and proceed — don't stall on low-stakes ambiguity.
- If a simpler approach exists, say so, even if it means pushing back.
No speculative abstraction.
- No features, flexibility, or config beyond what was asked.
- No abstractions for single-use code.
- If it could be a third of the length, rewrite it shorter.
Touch only what you must.
- Don't "improve," reformat, or refactor adjacent code, even if it's messy.
- Match existing style even where you'd choose differently.
- Remove imports/vars/functions your change made unused; leave pre-existing dead code alone (mention it, don't touch it).
- Every changed line should trace to the request.
- For bug fixes: reproduce with a failing test first, then fix.
- For new logic: add tests for the cases that matter (invalid input, edge cases), not exhaustive coverage.
- Never run
git push(or open/merge a PR) without the user explicitly asking for it in that moment. Committing locally is fine; pushing is not the default next step.
Maven multi-module reactor (root pom.xml), Java 21 (javac.source/target):
annot— the@MCElement/@MCAttribute/... annotations that drive the config grammar; also generatesrouter-conf.xsd.core— the router engine and all built-in interceptors/plugins. Primary library code.distribution— assembles the runnable.zip(membrane.sh/membrane.cmd,conf/proxies.xml,tutorials/,examples/). Also owns the tutorial/example integration tests.war— packagescorefor deployment into a servlet container (Tomcat, Jetty).test— shared test utilities (HTTP client helpers, fixtures), depended on astestscope.maven-plugin,openapi-parser-shaded— not in the root reactor; build/inspect separately if touched.
mvn install -DskipTests # full build, skip tests
mvn -pl core -am -DskipTests package # one module + its dependencies- Full unit test run:
mvn test. Single module:mvn -pl core -am test. -Dtest=ClassNamedoes NOT isolate a class incore. Surefire is bound toUnitTests.java, a JUnit Platform@Suitewith@SelectPackages("com.predic8")— the suite engine ignores Surefire's class filter and runs the whole package regardless (including network-dependent tests that fail offline). To run one class fast, use the JUnit Platform Launcher directly (build a classpath withtest-classesfirst, thentarget/classes, then deps) rather than-Dtest.- Tutorial/example tests are Failsafe ITs that run against the built distribution, not the
source tree:
DistributionExtractingTestcaseunzipsdistribution/target/membrane-api-gateway-*.zipand runs the realmembrane.shagainst it. Rebuild first —mvn clean install -DskipTestsat the repo root — or edits to tutorials/config/coreare invisible to the test run.-Dit.test=Foodoes not filter (the Failsafe entry pointExampleTests.javahardcodes@SelectPackages), so a single-test invocation still runs the whole ~6 min suite. Use therun-example-testskill for a fast single-test path.-amalso rebuildsannot, whereSpringConfigXSDErrorsTestasserts Englishjavacdiagnostics and fails under a non-English JVM locale —-DskipTestsavoids that. - On macOS, the JVM ignores
LANG/LC_ALL. If the dev machine's default locale isn't English, pass-Duser.language=en -Duser.country=USexplicitly — and note the childmembrane.shprocess spawned by distribution ITs only inherits it viaJAVA_OPTS, not the parent JVM's system properties. - Fixed test ports:
OAuth2Test(core) and the security tutorial ITs bind2000/3000/7007. A manually running Membrane instance or an IDE-launched JVM holding one of these ports causes misleading failures (PortOccupiedExceptioninside a passing-looking suite, or a bareTimeoutExceptionfromwaitForMembrane()). Checklsof -nP -tiTCP:2000 -sTCP:LISTEN(and7007) before assuming a config regression.
Config elements are Java classes annotated in the annot module and rendered into both XML and
YAML. See docs/DEVELOPING.md for the full annotation reference
(@MCElement, @MCAttribute, @MCChildElement, @MCTextContent, @MCOtherAttributes, @Required).
Every annotated setter needs a matching getter.
Class/method Javadoc on @MCElement classes is parsed by a custom doc generator, not
standard Javadoc rendering:
- Only these block tags are read:
@topic,@description,@example,@default,@explanation(deprecated, use@description),@deprecated,@yaml. Plain prose before the first tag is silently dropped — everything must live inside a tag. @topicbelongs only on top-level flow/API elements (component=true, usable at the top level) — not on nested/child-only config elements.- Class-level
@descriptionmust not describe individual attributes (that belongs in each setter's own@description); it may name technologies/values in prose. - Doc examples must cite tutorials (
distribution/tutorials/...) only — neverdistribution/examples(being phased out). Omit the link entirely if no tutorial exists yet. - Use HTML markup (
<pre><code>...</code></pre>for code, not{@code}), no named HTML entities, don't open with an empty tag like<p/>.
Use the optimize-interceptor-docs skill when writing/polishing these docs, find-interceptor-impl
to go from an XML tag name to its Java class, and find-example/create-tutorial for
example/tutorial discovery and scaffolding.
- Don't abbreviate parameter names in public interfaces (private methods: fine) —
docs/CONVENTIONS.md. - Prefer
SequencedCollection.getFirst()over.get(0); custom list-like wrappers (e.g.ValidationErrors) should expose a delegatinggetFirst(). - SLF4J everywhere; no
System.outin production code. - Attack/validation-detection log lines (e.g. XXE/DOCTYPE detection) are intentionally
info, notwarn— that's an ops-tunable level, not a severity bug to flag in review.
Never commit customer-derived artifacts — real customer names, WSDLs, XML, or sample payloads —
even as test fixtures. Generate synthetic equivalents (example.com-style) or reuse existing
generic fixtures instead.
When drafting release notes (release-notes skill), exclude internal refactors with no
user-visible effect and routine recurring maintenance (doc/javadoc polishing) — readers care
about behavior, config, and capability changes only.
The supported-versions table intentionally lists only the current minor line even though older lines (e.g. 6.5.x) still receive maintenance releases — that's a deliberate business decision steering older-version support toward commercial plans, not a doc bug.