Designing for Security
Trust boundaries, least privilege and capability-passing as design decisions in the code, distinct from the attacker techniques Security Engineering teaches.
Five questions — what is trusted, what is untrusted, who may call this, what data is sensitive, where privilege changes — asked before implementation, because afterwards they are structural changes.
A trust boundary is a line in your code, not a line on a network diagram: the place past which data is assumed clean, which is only true if one place made it so.
Least privilege is usually taught as an infrastructure setting. At code granularity it is a parameter type: a function handed a reader cannot write, and that is enforced rather than reviewed.
Instead of handing a module a service container and hoping, hand it the specific things it may do: CanSendEmail, CanChargePayment. Powerful, honest, and more ergonomic cost than most codebases will accept.
Which data is sensitive, where it is allowed to travel, and why a type beats a convention: a value that cannot be stringified cannot be logged by accident.