It’s easy to underestimate a front-end project's complexity, which leads to shallow and thus incorrect implementation. Attempts to fix this problem result in uncontrolled complexity growth and undefined behavior in corner cases.
We'll discuss ways of revealing the inherent complexity of a problem and dealing with it both on theoretical and practical levels.
3. • Who we are?
• Software Engineers @ Grammarly
• Working on …
• Past
• Browser Extension
• Present
• Web Editor
• Desktop Editor
4.
5. Our Talk: Overview
• [Done] Who we are
• => Overview (this slide :D)
• Complexity assessment mistake: why it happens?
• How to get it right?
• How to deal with complexity on the code level?
6. Our Talk
• [Done] Who we are
• [Done] Overview
• => Complexity assessment mistake: why it happens?
• How to get it right?
• How to deal with complexity on the code level?
7. 1. Why and how it happens
• People don’t understand the problem they are solving
• Incomplete requirements
• Edge cases not taken into account
• 3rd party libraries/components are being used
• …
9. 2. The process
• Planning and resource allocation
• Choosing approach, architecture, tools
• Coding
• Release :(
• …
10. 2. The process
• …
• Details appear in the process ‘out of the blue’
• It’s a small change!
• Let’s add one `if`
• [Repeat]
• Code => Spaghetti
• Release?
11. 2. The process
• …
• Details appear in the process ‘out of the blue’
• It’s a small change!
• Let’s add one `if`
• [Repeat]
• Code => Spaghetti
• Release… :(
Can you just add this button please?
Shouldn’t be too hard!
12. 2. The process
• …
• Details appear in the process ‘out of the blue’
• It’s a small change!
• Let’s add one `if`
• [Repeat]
• Code => Spaghetti
• Release… :(
Can you just add this button please?
Shouldn’t be too hard!
13. 2. The process
• …
• Details appear in the process ‘out of the blue’
• It’s a small change!
• Let’s add one `if`
• [Repeat]
• Code => Spaghetti
• Eternal release… soon :( Release… :(
14. 3.1. Technical Results
• Unsupportable, write-only code
• Instant legacy in a new product
• Undermining architectural effort; non-optimal solution
completely ruins the architecture
15. 3.1. Product Results
• Bugs
• Unexpected behavior
• A lot of product areas are underdeveloped
• Team’s moral & spirit is affected
16. Okay, smarty, what to do?
• Get complexity right
• Address it on the code level
17. Our Talk
• [Done] Who we are
• [Done] Overview
• [Done] Complexity assessment mistake: why it
happens?
• => How to get it right?
• How to deal with complexity on the code level?
18. Describe the problem in more details
• Talk to colleagues, specialists,
non-specialists, and to the debug duck
• Run a requirements testing with QA engineers
• Ask how to break a feature before you write it,
brainstorm corner cases
• Ask how a feature will develop in 1-2 years from now
• Look how it’s done by competitors, think why
19. Describe the problem in more details:Tools
• => Design mockups & prototyping
• Build models
• Plain old multileveled list
• Mind maps
• Draw a state chart (state diagram) with events and
transitions
• User story mapping
20. Describe the problem in more details:Tools
• Design mockups & prototyping
• => Build models
• Plain old multileveled list
• Mind maps
• Draw a state chart (state diagram) with events and
transitions
• User story mapping
21.
22. Describe the problem in more details:Tools
• Design mockups & prototyping
• Build models
• => Plain old multileveled list
• Mind maps
• Draw a state chart (state diagram) with events and
transitions
• User story mapping
23. Describe the problem in more details:Tools
• Design mockups & prototyping
• Build models
• Plain old multileveled list
• => Mind maps
• Draw a state chart (state diagram) with events and
transitions
• User story mapping
24.
25. Describe the problem in more details:Tools
• Design mockups & prototyping
• Build models
• Plain old multileveled list
• Mind maps
• => Draw a state chart (state diagram) with events and
transitions
• User story mapping
26.
27. Describe the problem in more details:Tools
• Design mockups & prototyping
• Build models
• Plain old multileveled list
• Mind maps
• Draw a state chart (state diagram) with events and
transitions
• => User story mapping (well, we don’t use it :))
28.
29. • …
• You name it!
Describe the problem in more details
33. Our Talk
• [Done] Who we are
• [Done] Overview
• [Done] Complexity assessment mistake: why it happens?
• [Done] How to get it right?
• => How to deal with complexity on the code level?
34. –Edsger W. Dijkstra
“The art of programming is
the art of organizing complexity”
–Anonymous
“Programming is all about composition”
36. Complexity?
• Wikipedia: In software engineering, programming
complexity is a measure of the interactions of the
various elements of the software.
• Level of entropy
• More: https://en.wikipedia.org/wiki/Complex_system
37. Where it came from?
• Essential complexity
• Input
• Output
• Accidental complexity
• Everything else we created
- Brooks, Fred (1986).“No Silver Bullet”
38. • Coupling
how tightly a module is related to others
• Cohesion
how closely all the routines in a module support it’s
central purpose
Characteristics
44. 1. Statements
• const vs let
Use const everywhere
• for, while, if, break =>
foreach, map, reduce, filter, find, ...
• Try, Option
Idiomatic handling for empty values and errors
• With this you mostly don’t need defensive
programming
46. 2. Functions/Methods
• Prefer pure functions
• Prefer polymorphism to if statements
• Single responsibility
• Don’t break invariants
• Use composition with functions.
They are first class citizens (thus: memoization, strategy,
pipeline, etc)
• Make it understandable without reading the method body
47. Prefer polymorphism to if statements
////// Don’t
function logMessage(message: string) {
remoteLoggerSink.send(message)
if (Config.printToConsole) {
console.log('LOG: ' + message)
}
}
////// Do
function logToRemote(message: string) {
remoteLoggerSink.send(message)
}
function logWithConsole(message: string) {
logToRemote(message)
console.log('LOG: ' + message)
}
const logMessage = Config.printToConsole ? logWithConsole : logToRemote
48. 2. Functions/Methods
• Prefer pure functions
• Prefer polymorphism to if statements
• Single responsibility
• Don’t break invariants
• Use composition with functions.
They are first class citizens (thus: memoization, strategy,
pipeline, etc)
• Make it understandable without reading the method body
50. 2. Functions/Methods
• Prefer pure functions
• Prefer polymorphism to if statements
• Single responsibility
• Don’t break invariants
• Use composition with functions.
They are first class citizens (thus: memoization, strategy,
pipeline, etc)
• Make it understandable without reading the method body
51. Make it understandable without reading the
method body
// Don't
function parse(data) {
// 20 LOC body to read
}
// Do
function parseConfig(configJson: string): Try<Config> {
// Whatever
}
60. Dependency inversion principle
namespace Bad {
class A {}
class B {
// Direct dependency creates stronger coupling
constructor(param: A) {}
}
}
namespace Good {
interface A {}
class B {
// Dependency on abstraction, allows any impl here
constructor(param: A) {}
}
class AImpl implements A {}
}
61. 3. Modules/Classes #2
• Data hiding/encapsulation
• Separation of concerns
• Composition over inheritance
• Invariance in types
• Algebraic data types
62. enum RequestState {
loading,
ready,
error
}
interface Loading {
state: RequestState.loading
}
interface Ready {
state: RequestState.ready
data: string
}
interface Error {
state: RequestState.error
error: any
}
type Request = Loading | Ready | Error
function proccesRequest(request: Request) {
switch (request.state) {
case RequestState.loading: return console.info('request in progress')
case RequestState.ready: return console.log('response', request.data)
case RequestState.error: return console.error('error =(', request.error)
default:
const state: never = request
throw new Error('Unexpected state')
}
}
Algebraic Data Types
64. 4. Subsystems/Apps
• Don’t reinvent the wheel:There is an app library for that!
• Grab a good runtime library
(ramda, immutable.js, lodash-fp, etc, still not clear for TS thou)
• Read more papers, e.g.
https://github.com/papers-we-love/papers-we-love
• Use existing design patterns which address your problem
• Use suitable data structures instead of arrays/objects
• Use frameworks/libraries that fit your task
72. Further reading
Articles on Requirements & Complexity
• Programming complexity https://en.wikipedia.org/wiki/
Programming_complexity
• Cyclomatic complexity https://en.wikipedia.org/wiki/
Cyclomatic_complexity
• No Silver Bullet http://www.cs.nott.ac.uk/~pszcah/G51ISS/Documents/
NoSilverBullet.html
• SOLID https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
• The Coming Software Apocalypse https://www.theatlantic.com/
technology/archive/2017/09/saving-the-world-from-code/540393
• User story mapping https://www.scrumalliance.org/community/articles/
2013/august/creating-an-agile-roadmap-using-story-mapping
73. Further reading
Useful papers, articles and tech
• Papers we love
https://github.com/papers-we-love/papers-we-love
• FP vs OOP
http://blog.cleancoder.com/uncle-bob/2014/11/24/
FPvsOO.html
• AirBnb Sketch App
https://github.com/airbnb/react-sketchapp
• FSM
https://en.wikipedia.org/wiki/Finite-state_machine
74. Further reading
Books and stuff
• Code Complete
• FRP https://www.manning.com/books/functional-reactive-programming
• Refactoring: Improving the Design of Existing Code
• UML Distilled: A Brief Guide to the Standard Object Modeling Language
• Head First Design Patterns
• Learning JavaScript Design Patterns
• TypeScript Deep Dive https://www.gitbook.com/book/basarat/typescript/details
• Functional JavaScript http://shop.oreilly.com/product/0636920028857.do
• FP in Scala https://www.manning.com/books/functional-programming-in-scala
• PureScript https://leanpub.com/purescript/read
• Learn You a Haskell for Great Good http://learnyouahaskell.com