- Declare opaque types as `typedef struct MyType MyType;` in public headers — define the struct only in the `.c` implementation file.
- Provide `mytype_create()` and `mytype_destroy()` functions for allocation and cleanup — callers never call `malloc`/`free` on opaque types.
- Use accessor functions (`mytype_get_name()`, `mytype_set_value()`) instead of exposing struct fields directly.
- Keep the full struct definition in the `.c` file so clients cannot depend on internal layout — this allows changing fields without breaking ABI.
- Pass opaque pointers as the first argument to all associated functions, mimicking method dispatch: `mytype_do_action(MyType *self, ...)`.
- Use a consistent naming convention: `module_verb_noun()` (e.g., `parser_read_token()`, `conn_pool_acquire()`).
- Group related opaque types into modules with a shared prefix — `http_request_*`, `http_response_*`, `http_server_*`.