software engineering best practices @ nylas
TRANSCRIPT
Software Engineering Best Practices
Ben Gotow (@bengotow)
Computer Science ≠ Software Engineering• Clean
• Predictable
• Maintainable
• Extensible
• Fast & Efficient
• Mathematically Optimal
• Uses latest language features
Essentials• The Mafia Model
• Design Patterns
• Building a Shared Language
• Exceptions aren’t evil
• Anti-patterns
“Design software like you’re running a mafia.”
• Has one well defined job. Doesn’t ask questions.
• Has limited knowledge of the rest of the operation. Others don’t know or care how he gets the job done.
• Has no secret dealings with others, clean cut actor.
• Can be replaced—others could do this specific job if he was “removed.”
• Single Responsibility Principle (Robert C. Martin)
• Separation of Concerns (Dijkstra)
• Avoid Side Effects
• “Program to an interface, not an implementation.” (Erich Gamma)
Design Patterns
Patterns make it easy to communicate abstract ideas about software.
Design PatternsCommand EmitterSingleton
Design PatternsCommand
Encapsulate a request as an object, thereby letting users parameterize clients with different requests, queue or log
requests, and support undoable operations.
(Tasks in N1 are an implementation of the Command pattern.)
Anti-Patterns (“Code Smells”)- Copying code into more than three places
- Calling private methods on other classes
- Inspecting state which was not designed to be observed
- Waiting for something to happen using the wall clock
setTimeout I’m looking at you.😒
Anti-Patterns (“Code Smells”)Ex: Adding optional parameters that change the behavior of existing code slightly. “Don’t do this, this one time.”
thread.save({tellUser: true}) thread.save().then(() => { this.tellUser();});
Take a break, think about refactoring, explain the problem to someone else.
Naming Conventions
Naming things is hard, and really important when building software on a team.
• Functions that return a value should indicate how costly that value is to retrieve:
thread() // easygetThread() // hmm, probably not O(1)fetchThread() // better cache the result of this!
Naming Conventions
• Names should reflect intended use and give you an idea what is returned:
onClicked() // called in response to an eventnewWindow() // always returns a new objectisSending() // always returns a booleanensureReady() // may or may not do anything
• Long names are almost always worth it:finalizeAndPersistNewMessage() // shit is going down
Naming Conventions
• Functions with many parameters should use named hashes: _onComposeReply: ({thread, message, popout, behavior}) =>
• Leading (or trailing) underscores denote private members:_doInternalFileWrite myInstanceVar_
this._onComposeReply({ thread: A, behavior: reply-all, popout: true })
Naming Conventions
Exceptions aren’t EvilThrow exceptions aggressively to protect the code you write from things it wasn’t intended for.
• If your code makes assumptions, make assertions.
• If you ever have the option of crashing now, or /probably/ crashing later downstream, crash now.
Exceptions aren’t Evil
😍
Code Review
https://paper.dropbox.com/doc/Code-Reviews-using-Phabricator-LdupUMb1X9SWMyrShmhQB
Write better code, learn new tricks, avoid shipping mistakes, develop a shared understanding of the codebase.
Code ReviewCode Level:
• Clarity • Function / variable naming • Test coverage
Architectural Level:• Does this fit performance requirements? • Does this follow conventions and patterns used elsewhere? • Are we comfortable with the limitations of this approach? • Are changes well contained?
Further Reading- Design Patterns: Elements of Reusable Object-Oriented
Software (Gang of Four)
- Clean Code: A Handbook of Agile Software Craftmanship (Bob Martin)
- https://sourcemaking.com/design_patterns
- http://en.clouddesignpattern.org