Monday, February 15, 2016

Zen of Software Design/Python



http://venkateshcm.com/2014/04/Zen-Of-Software-Design-Part-1/
>>> import this
The Zen of Python, by Tim Peters

1 Beautiful is better than ugly.
2 Explicit is better than implicit.
3 Simple is better than complex.
4 Complex is better than complicated.
5 Flat is better than nested.
6 Sparse is better than dense.
7 Readability counts.
8 Special cases aren't special enough to break the rules.
9 Although practicality beats purity.
10 Errors should never pass silently.
11 Unless explicitly silenced.
12 In the face of ambiguity, refuse the temptation to guess.
13 There should be one-- and preferably only one --obvious way to do it.
14 Although that way may not be obvious at first unless you're Dutch.
15 Now is better than never.
16 Although never is often better than *right* now.
17 If the implementation is hard to explain, it's a bad idea.
18 If the implementation is easy to explain, it may be a good idea.
19 Namespaces are one honking great idea -- let's do more of those! 
Beautiful is better than ugly

Explicit is better than implicit.
It should be obvious to figure-out what’s happening in code, even for someone not familiar with the codebase. Usually implicit design makes it look like black magic without any clue how it is working or where to look for related code. There are two kind of implicit we face in day-to-day work.
  • Well known implicit :– Things which are well documented and have become standard way of doing things. These implicit usually go across project team boundaries. Example : SpringMVC annotations, Rails conventions
  • Project implicit :– Things which project teams builds within the project which is not something a new developer would be acquainted with in other projects.
I have seen developers going overboard with Annotations or conventions in projects, where it is difficult to figure-out how things work. A rule of thumb I use to figure out if we have gone overboard is by asking myself the question “Can I explain, to a new developer joining team, all the implicit things happening in the project within 1 hour session?”.
Simple is better than complex.
Few questions which help me be on track of simplicity
  • How many execution flows/paths exists?
    if there are few execution paths than its easy to keep them in mind and figure out a specific scenario falls in which one of the paths.
  • How easy is it to figure-out a given scenario falls under which execution path?
    It should be easy to figure that out, if the application is well designed and simple to remember.
  • How many exceptions flows exist as against conventional flows?
    In every application there are few exceptional scenarios which don’t fall under normal execution paths and need exceptional flows for them. These should be minimal and explicitly identified.
  • How easy it is to explain high level design to a new developer?
    I found this rule of thumb to be very helpful. It becomes obvious when you explain (or imagining to explain) how application works to a new developer. If you can explain it without flinching several times when the developer says “ah in scenario x app will do this” and you go hmmm its mostly correct but there are some other issues etc.
Complex is better than complicated.
Design can be complex because domain is complex but sometime domain is simple but software is designed or implemented in complicated way. Design should be as-complex or as-simple as the requirement need, not more and not less.
The questions I ask to figure out if design is complicated is
  • What part of requirement is causing the design to be complex?
  • Is the technology choice introducing additional complexity?
  • Is there any negotiable requirement which can be removed to make it more simpler?
  • Are we designing the system for future proofing which might not be required?
Flat is better than nested.
When we look at code there are few obvious cases where this is true. For example it is better to have several flat functions than having one long nested function. For example :– Using Strategy Design Pattern or Command Design pattern to separate code into independent units. Where it is not very obvious and still useful to think is
  • How many layers are we introducing in software architecture?
  • How many jumps do we need to make to get to the final value (may be cached value or database lookup) ?
  • Is the path of execution intermingled and can not be modified independently?

Sparse is better than dense. Readability counts.
Few times I have questioned myself if single responsibility principle, unit testing, dependency injection and design patterns are good. The reason for this doubt is that after following single responsibility principle, unit testing with dependency injection and following design pattern, a small codebase grows to considerable code size. I call this new increased codebase sparse compared with earlier code which was dense.
The advantages of sparse code base compared to dense code is
  • It is easier to understand and modify.
  • It is easier to extend to add more scenarios.
  • It is easier to isolate an issue and fix it.

Special cases aren’t special enough to break the rules. Although practicality beats purity.
Every now-and-then a new requirement which breaks design rules under which application has operating comes in. Usual tendency is to treat this as special case which works differently to existing architecture.
While it might be the right approach in few cases, we should
  • strive to see if this special case can be modelled as one of the existing flows
  • check if existing rules can be extended to incorporate the new requirement as first class design decision instead of treating it as special case which breaks the rules.
  • check if we have been adding a lot of special cases and do a course correction if required.
On the other hand if they truly are special cases and trying to extend or modelling it has one of the existing flows make design complicated, we should not hesitate to incorporate the new requirement as special case as a pragmatic/practical architect or developer should do.
Errors should never pass silently.
Unless explicitly silenced.
Silencing an error is hiding a symptom. Catching an exception and ignoring it is hiding error from being discovered and providing false feedback to user. Application should make errors transparent and visible. i.e. End user should know that the current task has failed and he had to take appropriate action and not proceed with assumption that the task has succeeded.
That said, in few occasions when a low priority task is performed along with high priority task, it is better not to fail the entire request if low priority task fails.
For Example:– User registration might have two steps
  • Update user information in datastore
  • Send out an confirmation email
If user information is updated in datastore and sending confirmation email failed it is ok to ignore send email error by queuing it to be processed later. But this also means taking appropriate steps to intimate support person and to make sure developer has enough information to figure out the reason for failure.
In the face of ambiguity, refuse the temptation to guess.
Although that way may not be obvious at first unless you’re Dutch.
In large systems it is difficult to figure what went wrong, without testing and inspecting several scenarios. I have seen developers jump into code to fix an issue without understanding the root caused for error and later to realise they were barking on wrong tree. It is better to simulate the error and than find a solution to fix the error, instead of working on a solution directly. After simulating error we could test out different hypothesis to confirm the assumptions.
Now is better than never.
Although never is often better than right now.
When faced with a need for large scale code refactoring, we tend to procrastinate and postpone the refactoring. Even thought we know the urgency or necessity for refactoring and how to fix the problem, we keep broken windows in codebase causing more issues.
There are cases where we don’t have a solution for the problem or we don’t have enough time to make the change right now. It is better to hold off changes, instead of doing half baked (partial) solution right now. In many cases it is better to hold off doing the changes until the right opportune moment, instead of jumping into action right away. (Eg :– Premature optimisation)
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
If the business problem is not modelled correctly or there are several exceptions each core application flows. Software design tends to be hard to explain. In such cases, hard to explain, should be treated as a design-smell.
Difficulty to explain means bad design but easy to explain does not imply good design. Simple design might not have taken all different scenarios into consideration or could have made plain wrong assumption.
Design should be as-complex or as-simple as the requirement need, not more and not less. Over designing is adding unnecessary complexity to the system and under designing is not handling all scenarios.
Namespaces are one honking great idea — let’s do more of those!
There are several tools and conventions which make life easier, we should just follow them instead of reinventing new solutions. Following conventions and standards is very important means of managing code quality.

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts