Here are a few points to keep in mind that I just threw together and would love some feedback on.
Prefer functionality over implementation. Protocols tell you what a construct does and not how it does it. Focus your code on the connections between data creation and data consumption instead of the particulars of specific types.
It’s never too late to refactor. While it’s great to write generics and protocols right out of the box, it’s extremely common to develop code and then consider how to retrofit.
Watch out for repeated code across different types. Repeated code segments with just a few type-specific changes hints at patterns that lend themselves to generic and protocol implementations. Focus on the commonalities of design patterns to find targets of opportunity.
One protocol does not rule them all. Keep your protocols short, sweet, and meaningful. Each one should be a noun (often ending with “Type”) or a verb (ending with “ible” or “able”). Think this is this a kind of thing and this can do this. Avoid overloading protocols with too many semantics that distract from a protocol’s one true calling.
Differentiate type constraints from parameter constraints. Refactor functions with extensive angle bracket clauses to use protocol extensions and methods. Swift 2.0’s protocol extensions use where clauses to constrain where methods apply, enabling you to move those type constraint clauses away overloaded angle-brackets and into a more meaningful context.
Conform at the highest possible level. When adding protocol conformance and default implementations, do so at the highest possible abstraction that still makes sense. Instead of adding separate adoptions for, for example, Int and String, check whether there is a unifying concept that can be introduced at a common protocol they both already adopt. If this becomes too general, create a new protocol, conform the types that apply, and maybe even add an extension to implement the generic behavior you’re looking for.
Design with collections in mind. Differentiate whether you will work with heterogeneous (same protocol) or homogeneous (same type) functions and design your protocols correspondingly. Self requirements enable you to add same-type constraints. Protocol names support common conformance.