I write both unit tests and integration tests. My tests are about 75% of the code base, but finding (and fixing) bugs is super easy this way.
Unit tests test functions as code, not as functionality. I try to write tests for all possible branches of code. This may sound cumbersome, but most of the tests are boilerplate, for what i have code snippets in my editor. As there are a lot of functions that modify the database, i always run unit tests in a separate environment and/or with the ORM being mocked. These are important as if i need to change tests due to code change, it makes me take a good look if all callers of the changed function is updated.
Integration tests are harder to write. I usually use BDD (Behaviour Driven Development) with a natural-like language, so even non-tech colleagues can write tests. A lot of these tests are destructive so they run in an isolated environment, but in an infrastructure identical to the production one.
As a result i barely introduce bugs in the code (the business model is a different story, though). As a con, it takes time to create the necessary environments, but i wouldn’t daresay it doesn’t worth it.