Not verified hands-on. This article was written from primary documentation rather than reproduced in an org, so it carries no tested-release badge. If you run it and something differs, say so in the comments and it gets tested and corrected.

The short version: 75% org-wide is a deployment gate, not a quality target, and it is trivially satisfied by tests that assert nothing. The useful question is not what percentage you have but whether a test would fail if the code were wrong. Those are different properties and only one of them is measured.

Coverage and correctness75% is a deployment gate, not a quality target, and the two are measured differently.What the gate measures75% org-wide to deploy. Satisfied by tests thatassert nothing.Org-wide, not per class: one well-coveredclass carries untested code past the gateEvery trigger needs some coverage, which isa separate ruleManaged package code counts in neitherdirectionRemove a method body and a no-assertion teststill passesWhat a test should proveThat it would fail if the code were wrong.Different property, not measured.The output, against a number you worked outby handThe side effects, including the things thatshould not have changedThe failure path, with System.assert(false)after the call that should throw200 records, not one — a query in a loopcovers the same lines either wayA class at 100% with the assertions removed is in a worse state than a class at 76% whose tests checkthe arithmetic. Read which lines are uncovered, not the percentage.
What the gate measures, against what a test should actually prove.

What the number actually requires

  • 75% across the org to deploy to production. Not per class.
  • Every trigger needs some coverage — more than zero lines, which is a separate rule.
  • Managed package code does not count toward your total, in either direction.
  • Test classes themselves are excluded from the calculation.

So a single class with 5,000 well-covered lines can carry a lot of untested code past the gate. That is not a loophole to use, it is an explanation of why an org at 82% can still have critical logic with no test at all.

The test that covers everything and proves nothing

@IsTest
static void coverIt() {
    Account a = new Account(Name = 'x');
    insert a;
    AccountService.recalculate(a.Id);
}

This runs the code, so the lines are covered. It asserts nothing, so it passes whether recalculate computes correctly, computes wrongly, or does nothing at all. Remove the body of the method and the test still passes.

A test with no assertion is worse than no test, because it produces a green build and a number that satisfies the gate. If you find these, the honest first step is to add assertions, not to delete them — deleting drops your coverage and the gate is real.

What to assert

Three things, in order of value:

The output. The value returned, or the field set, compared against a number you worked out by hand. Not against the same calculation re-implemented in the test, which passes even when both are wrong.

The side effects. If the method is supposed to create a task, query for it. If it is supposed to leave something unchanged, assert that too — the tests that catch real regressions are often the ones asserting nothing happened.

The failure. Call it with input that should be rejected and assert the exception type and something about the message. Around half of production incidents are a failure path that was never executed once.

try {
    OrderService.submit(orderWithNoLines);
    System.assert(false, 'expected an exception for an order with no lines');
} catch (OrderValidationException e) {
    System.assert(e.getMessage().contains('at least one line'), e.getMessage());
}

The System.assert(false, ...) after the call is what makes this a real test. Without it, a method that stops throwing passes silently.

Bulk is a correctness test, not a coverage one

Insert 200 records, not one. It costs one line and catches the entire class of bug that only appears under Data Loader, because a query inside a loop covers exactly the same lines with one record as with two hundred. The limits it protects against are per transaction, and they are in the governor limits reference.

Reading the coverage report usefully

The Developer Console and sf apex run test --code-coverage both show uncovered lines. Look at which lines rather than at the percentage. Uncovered catch blocks, uncovered else branches and uncovered null checks are the ones worth fixing, because they are the paths that run when something goes wrong, which is when you most want the code to be right.

A class at 100% with every assertion removed is a worse state than a class at 76% where the tests check the arithmetic.

What to stop doing

  • SeeAllData=true unless the API genuinely requires it. It makes tests depend on org data that changes, and a sandbox refresh breaks them.
  • Testing getters and setters to lift a percentage. It adds maintenance and finds nothing.
  • One enormous test method covering a whole class. When it fails you learn that something broke, not what.

Frequently asked

Does a deployment run all tests? Depends on the test level. RunLocalTests excludes managed packages and is the usual choice. Production deployments of Apex require tests regardless.

Why did coverage drop after a deployment I did not change? Coverage is calculated from the last run, so new uncovered code anywhere moves the org-wide number. It is a shared total.

Is 100% a goal? No. Some lines are not worth the test that would cover them. Aim for every branch that decides something.