# Cloud-Ready Backend Code: Quality Gates and Common Pitfalls

On AEM as a Cloud Service, your code reaches Production only through a **Cloud Manager pipeline**, and that pipeline checks it. Some checks are about quality, some about compatibility, and some about patterns that are known to cause outages. Teams moving from AEM 6.5 often find that code that ran for years now fails the build. This post collects the rules and habits that make backend code **Cloud-ready**, so you can write it right the first time or find what to fix when migrating.

## What the Pipeline Checks

A Cloud Manager pipeline builds your project, runs your unit tests, and then runs **code quality scans** (static analysis plus Adobe's own AEM-specific rules) and compatibility checks against the AEM SDK API. Findings are rated by severity. **Critical** findings stop the pipeline, while lower severities are reported for you to address.

Because rule names and thresholds change over time, treat the lists below as **patterns to know**, and read the current Cloud Manager documentation for the exact rules.

## Patterns the Quality Rules Target

| Pattern | Why it's a problem | What to do |
| --- | --- | --- |
| **A** `ResourceResolver` **that isn't closed** | Leaks resources and degrades the system over time | Use try-with-resources, as in the service users post |
| **HTTP calls without connect and read timeouts** | One slow partner can stall the whole site | Set both, with values far below the 60-second maximum, as in the integrations post |
| **Servlets registered by path** | Bypass the standard access control on resources | Register by resource type |
| **Dangerous Java calls** (for example stopping threads or killing the JVM) | Can destabilize a shared runtime | Remove them. There's almost always a safer design |
| **Using the Sling Scheduler for guaranteed work** | Runs on every instance, or not at all, on a cluster | Use Sling Jobs |
| **Implementing product APIs meant to be consumed** | Breaks when Adobe updates the platform | Call them, don't implement them |

## Design Rules for a Cluster

These are not all machine-checked, but they decide whether your code behaves in production.

**Be stateless.** Don't keep important data in instance fields or on the local disk. Another instance won't have it, and the instance itself can disappear at any time.

**Don't write to the local file system.** Treat it as temporary. Store anything that matters in the repository or in an external service.

**Assume several instances.** Anything triggered by an event or a timer may run on more than one. Use jobs for "once" semantics, and make the work idempotent.

**Expect old and new code to overlap.** During a deployment, instances running the previous release and the new one serve traffic together for a while. Don't change a content structure or an API in a way that breaks the other version. Do such changes in two steps: first support both, then remove the old.

**Don't depend on startup order.** Services can appear in any sequence. Use optional or dynamic references where a missing service shouldn't be fatal.

**Don't write to immutable areas.** `/apps` and `/libs` can't be changed at runtime. Write to `/content`, `/conf` or `/var`.

## Things That Change When Moving from 6.5

| On 6.5 you could... | On Cloud Service you... |
| --- | --- |
| Install a package or bundle by hand | Deploy through the pipeline |
| Edit OSGi configuration on the server | Commit `.cfg.json` files and deploy |
| Use `loginAdministrative` | Use service users |
| Run a Commons Scheduler task | Use Sling Jobs |
| Keep files on the disk | Use the repository or an external store |
| Use any API in the product jars | Use the **AEM SDK API** |
| Use custom run modes | Use `author`, `publish`, and `dev`, `stage`, `prod` |
| Rely on replication agents | Use the built-in content distribution |
| Add libraries freely | Check each library is allowed and resolvable. Bundled third-party code may need review |

Adobe's tools help with the audit. The **Best Practices Analyzer** scans a 6.5 instance and reports incompatible patterns, and the **pattern detector** flags specific code patterns that need changes. Run them early in a migration.

## Java Version Differences

Cloud Service has moved to newer Java versions over time, and each step removes some older APIs. If you're upgrading a project:

- Check which Java version your pipeline builds and runs with
- Replace removed or restricted APIs
- Update libraries whose old versions don't support the target version
- Be careful with javax and Jakarta package names. Most AEM projects still use `javax` imports, and the move to Jakarta is only now arriving, so don't change imports unless your SDK version and archetype tell you to

## Test Like the Pipeline Does

Running the checks locally saves a pipeline run:

```bash
mvn clean verify
```

Then:

- Fix unit test failures first
- Read warnings about unresolved imports or API usage. They usually mean something won't resolve at runtime
- Deploy to a **Dev environment** before Stage, and watch the logs through one full cycle of the background work

## A Practical Review Checklist

Use this in code review for any backend change:

1. Every `ResourceResolver` is closed, or comes from the request
2. Every outbound HTTP call has timeouts
3. Servlets use resource types, not paths
4. No new Commons Scheduler tasks for important work
5. Background work is idempotent and runs through Sling Jobs
6. Configuration comes from `.cfg.json` with run modes and environment variables, with no secrets in Git
7. No local file writes, no static mutable state of importance
8. No assumptions about instance count or startup order
9. Logging has no secrets and uses the right levels
10. A unit test covers the main path and one failure path

## Common Pitfalls

- **"It passed locally."** The local SDK is one instance with relaxed behavior
- **Fixing the warning but not the cause.** For example catching an exception to silence a rule
- **A big-bang change.** Large, risky deployments are harder to roll forward safely than small ones
- **Ignoring non-critical findings forever.** They accumulate and later block you
- **Skipping the Dev environment.** Cluster behavior only shows up there

## What's Next

You've now seen the pieces: OSGi, configuration, service users, servlets, jobs, workflows, integrations, security and Cloud-ready habits. The final post puts them together into one **worked backend example**.

---

*Next in this series: \[Putting It Together: A Worked Backend Example\]*
