Question: There're certain things in code that can't be conditional. Two very common examples:

* static types
* dependencies.

You can't naturally enable/disable a feature that implies a new dependency or a new type with a condition (a feature flag).

How do the proponents of trunk-based development tackle such cases?

1. `package.json`

Suppose a new feature requires a new dependency. Now 2 versions have to somehow coexist in `main` and be controlled by some ENV var. DIY machinery around `package.json` and logically nested versioning around this file sounds very dangerous. You basically reimplement low-level VCS-like functionality for a single file.
2. Types

This can be mitigated to some degree in some languages but not in the others.
For example in TypeScript, types can be dynamically inferred so it's possible to:

```
export const foo = FEATURE_ENABLED ? _newFoo : _oldFoo
export type Foo = typeof Foo

```

Still it's pretty messy and does not answer how to approach the same problem in other statically typed languages.

Answer: 
Feature flags should be used mainly for controlling the *visible behaviour* of a system from the outside. Introducing new types, or adding a dependency should not change that behaviour immediately, as long as the new type or code from the dependency are not used somewhere. And the usage itself can be controlled by a feature flag.

Modifications to existing types and dependencies can often be handled by keeping the old and new types in parallel in the code as long as the feature flag exists, or by keeping the old and new dependency both in the code, where this is possible.

Of course, when your software makes use of reflection to activate some new feature automatically as soon as a new type or dependency is available, then you need to work around this. But such mechanics is usually implemented dynamically and can hence be handled easily at run time.

**However** - in your comments, you asked: what about

1. Introducing new versions of existing dependencies, where you cannot easily install two versions in parallel, and the new dependency is not fully backwards compatible?
2. Introducing new versions of existing types, where maintaining these two versions in parallel causes some cascading effekt throughout a huge part the code base?

For those cases I think feature flags are indeed not the ideal tool. Separate branches give you the chance to develop and test some code to deal with some incompatiblities first in isolation, which gives you a lower risk of breaking anything before merging it back into the trunk. The disadvantage is that you may have more work to resolve merge conflicts. Hence it is sometimes good to have two different tools in your toolbox and know when to choose which.

Let me add I have a lot of first-hand experience with both approaches. Where I work, we handle at least 95% of our feature development with feature flags, and maybe 5% or less by feature branches. But YMMV.
I am more backend-oriented but what we do with Java is just update the the library and static objects in a compatible way.

Example:

* Update a core library: just add the new version. If this is backward compatible there is no need to do anything special. Let's say that it is a new incompatible version. Now you will need to create a commit that updates any changes to make it compatible. Your test suite should be able to detect any issue in local. If not, you need to consider if the project desired level of availability allows you to use trunk based development.
* Update an static type: I am not 100% sure of what do you refer but lets assume that you are adding a new field to the DTO -> Domain -> Database. You can add the new field to the DTO as optional without breaking the contract. You can update the database and create it as nullable with null as default value. You will need to update both database in a backward compatible way. Once that you update the code it may return the new field if stated and this should be backward compatible. On the long run you can check that all values are filled you can change to a non-null value. For more drastic changes you may need to a new version of the endpoint.

Remember that trunk based development requires good observability to detect issues in production and the ability to rollback fast to a previous version. Some deployment patterns like canaries, dark deployments and similar may help you to be safer.