These are the methods we use every day on our own internal system and website, and the same ones we apply to client projects.
A separate test environment, with the live system read-only
Each system has two copies: the live one serving real users, and a test copy with its own database of fake sample data. Anything that writes data is tried on the test copy. On the live system, engineers only read while investigating a defect. Clients review each iteration on this test environment, so they never have to test on their own real data.
Automatic tests for calculation rules and permissions
The most error-prone part of business software is calculation: pay, insurance, tax, commission, dealer pricing. Each rule has test cases with hand-calculated answers, including edge cases such as contribution ceilings, public holidays next to weekends and leap years. Permissions are tested by calling each API route with an account that lacks access and requiring it to be refused. Every publicly open route must be declared in its own test, so a route cannot be opened by accident.
Positive controls: a measuring tool must catch a planted defect
A tool that reports zero defects can mean two things: there are none, or the tool cannot see. Both print the same line. So each measuring tool we write has a control mode that plants known defects where it measures, requires each to be caught, then removes them and requires silence. Until it passes the control, no number is reported.
Measure behavior, not status
A correct configuration does not mean correct behavior. A header declared sticky can still scroll away, and a process reported as running can still return an error page. So our checks do what a user does: scroll to the middle of an article and measure the position, click the button and read the result, call the public address and read the response code. An empty result is treated as a sign that the tool is broken before it is treated as proof of no defects.
A gate before every commit
A hook inside the code repository itself blocks known mistakes before code is written into the history. Because it lives in the repository, it runs for every person and machine, regardless of personal settings.