Thursday, April 21, 2016

How to Be a Better Programmer - Part 3

1. The instinct to experiment first

The compiler and runtime can often answer a question faster than a human can. Rather than seek out a senior programmer and ask them "will it work if I do this?", a good programmer will just try it and see if it works before bringing their problem to someone else.
  1. 代码还没运行就能预知其中的 bug。

2. Emotional detachment from code and design

Code is like kleenex: you use it when it's useful and throw it away when it no longer serves. We all like to think that code-reuse is important, and while it is, it's not meant to be about raising a child. Code doesn't feel. Code doesn't care. Code will turn on you like a Frankenstein monster. Code is just bytes. Code is a liability.


  1. Almost no committed code that is commented out
  2. Willingly throws away weeks or months of work in order to adopt another programmer's superior code
  3. Puts finger to lips, furrows brow and says "hmm" when faults in their work are pointed out, while looking at the code and not the critic
  4. Indifferent to the way the IDE wants to auto-format code, uninterested in "tabs-vs-spaces" arguments
  5. Refers to it as "the code" rather than "my code", unless accepting blame
  6. Has abandoned a design of theirs that was previously used in a successful product
  7. Doesn't become defensive when the boss mentions that they're looking for an off-the-shelf alternative to what they've been writing for the past few years

it's you who controls the idea, not the idea that controls you.

Find the code that you're the most proud of and delete it, now re-write it from scratch in a different way. Use a "design pattern" that confuses you, or that you hate (e.g.: the Singleton) and figure out how to make it work. If necessary, delete that after you've got it working and try again with a new pattern or language. Not only will you learn that there's More Than One Way To Do It, but you'll learn that your code is transitory. Code, by its nature, is not just inextricably glued to its language, platform, and the APIs it consumes, but written in the form of ephemeral static charges, orientations of magnetic particles, subject to the whims of the market, Moore's Law, and your employer.

Other techniques to break the abusive relationship:
  1. Maintain somebody else's code
  2. Experience, either by accident or bloody intention, what it's like to lose a week's work to a failed backup or a botched commit and have to re-write it all over again
  3. Work for start-ups where you'll get laid-off when the second or third round of financing doesn't come through
  4. Be stupid enough to post your best code on Reddit

3. Eager to fix what isn't broken

Programs are infrastructure: they're built to serve a specific need, but needs always change. Good programmers realize that hard-coded values buried in code are bad, that a destoryBaghdad() function is immoral, and that it's a priority to eliminate "code smells". Not for pride. Not for backslapping attaboys from your peers or the authors of methodology books. But because you will itch until it is fixed.

  1. Doesn't take the spec by its word and tries to find out who wrote it and what they were thinking
  2. Hunts down and talks to the people who will use the program each day
  3. Owns a book written by a guy called Martin Fowler
  4. Tends to express extreme like or dislike for popular technologies such as XML, ORM and REST, and has also switched positions on one or more of these in the past
  5. Likes to use abstraction layers, but doesn't like to add more than one layer on top of what's already in the language or platform
  6. Talks about "low cohesion"
  7. At least 10% or more of their commits reduce the line-count of the project without adding new functionality
  8. Before adding a new feature, checks to see if an existing one can be re-designed to perform both tasks or replaced entirely with a better method
Code lets you learn in stages where you don't need to re-write everything from scratch. You re-write pieces after you understand what they need to do and what they'll never need to do, make them simpler, shorter and beautiful.

Aim for these, in increasing order of importance:
  1. Code that does the same thing, but is shorter or more efficient
  2. Code that does the same thing, but uses an appropriate "wheel" built-into the platform instead of reinventing its own
  3. Code that does the same thing, but is easier to modify for similar needs
  4. Code that does the same thing, but is easier to read and understand
  5. Code that doesn't exist

4. Fascinated by the incomprehensible

5. Compelled to teach

  1. Blogs about their work
  2. Has an active Wikipedia account
  3. Unhesitant to pick up a marker and approach a whiteboard
  4. Commits changes to the repository that consist only of comments
  5. Lets new hires borrow books that cost them $100 to buy

1. Incorruptible patience

  1. Unbothered by office politics
  2. Can predict a bug before the code is ever run

2. A destructive pursuit of perfection

3. Encyclopedic grasp of the platform

Encyclopedic knowledge takes decades to acquire, but every Guru in the world got there by doing roughly the same three things each day:
  1. Struggling to solve problems they find to be difficult
  2. Writing about how they solved difficult problems
  3. Reflecting on how they solved difficult problems

4. Thinks In Code

5. When In Rome, Does As Romans Do

  1. No automatic interest in cross-platform frameworks
  2. Contemptuous of "language wars"
  3. Doesn't see a strategic disadvantage in maintaining the same program in multiple languages
  4. Assumes their own code is the source of a bug before blaming the compiler, library or operating system
  5. Displays a plush Tux penguin or Android in their cubicle soon after being assigned to a project targeting that platform
  6. Switches brand of cell phone or tablet in the same circumstance
  7. Hits a stack of technical manuals before assuming a data-type like double or decimal will do what they think on a new device

6. Creates their own tools

  1. Has set up an automated build server
  2. Has written their own benchmark or specialized profiler
  3. Maintains an open-source project on GitHub
  4. Has re-invented LISP at least once
  5. Knows what a Domain Specific Language is, and has designed and written an interpreter for one
  6. Extends their IDE/Editor with custom macros

Signs that you're destined for more

1. Indifferent to Hierarchy

Richard Feynman once pointed out that "it doesn't matter who your dad knows", if something is wrong then it's wrong no matter who says its right. Don't fear the consequences to your career, you''ll find another job. Society never wastes real talent.
  1. Getting into arguments with the CEO
  2. Quitting on principle
  3. Organizing teams without permission
  4. Creating new products after-hours while hiding from the Rent-a-Cops
  5. Re-organizing the workspace "Peopleware" style, against company policy
  6. Helps themselves to the boss's private stash of bottled water

2. Excited by failure

3. Indifferent to circumstances

  1. Stock options and bonuses are ineffective retainment techniques
  2. Cashes-in their 401k to fund their next venture

4. Unswayed by obligations

5. Substitutes impulse for commitment

6. Driven by experiences

The idea of recursion is easy enough to understand, but programmers often have problems imagining the result of a recursive operation in their minds, or how a complex result can be computed with a simple function. This makes it harder to design a recursive function because you have trouble picturing "where you are" when you come to writing the test for the base condition or the parameters for the recursive call.


  1. Hideously complex iterative algorithms for problems that can be solved recursively (eg: traversing a filesystem tree), especially where memory and performance is not a premium
  2. Recursive functions that check the same base condition both before and after the recursive call
  3. Recursive functions that don't test for a base condition
  4. Recursive subroutines that concatenate/sum to a global variable or a carry-along output variable
  5. Apparent confusion about what to pass as the parameter in the recursive call, or recursive calls that pass the parameter unmodified
  6. Thinking that the number of iterations is going to be passed as a parameter


Get your feet wet and be prepared for some stack overflows. Begin by writing code with only one base-condition check and one recursive call that uses the same, unmodified parameter that was passed. Stop coding even if you have the feeling that it's not enough, and run it anyway. It throws a stack-overflow exception, so now go back and pass a modified copy of the parameter in the recursive call. More stack overflows? Excessive output? Then do more code-and-run iterations, switching from tweaking your base-condition test to tweaking your recursive call until you start to intuit how the function is transforming its input. Resist the urge to use more than one base-condition test or recursive call unless you really Know What You're Doing.
Your goal is to have the confidence to jump in, even if you don't have a complete sense of "where you are" in the imaginary recursive path. Then when you need to write a function for a real project you'd begin by writing a unit test first, and proceeding with the same technique above.


  1. 对问题设计极其复杂的迭代算法,但其实可以通过递归解决(比如:遍历一个文件系统树),尤其是在不用保证内存和性能的情况下。
  2. 递归函数在递归调用前后都会检查相同的初始条件。
  3. 递归函数没有测试初始条件。
  4. 递归子程序连接到一个全局变量或支持输出的变量上,或者累计这些变量的和。
  5. 对于递归调用中要传递什么参数表现出明显的困惑,或是不理解传递未修改参数的递归调用。
  6. 认为迭代的次数会被作为参数传递。

2. Lack of critical thinking

Unless you criticize your own ideas and look for flaws in your own thinking, you will miss problems that can be fixed before you even start coding. If you also fail to criticize your own code once written, you will only learn at the vastly slower pace of trial and error. This problem originates in both lazy thinking and egocentric thinking, so its symptoms seem to come from two different directions.
Start with a book like Critical Thinking by Paul and Elder, work on controlling your ego, and practice resisting the urge to defend yourself as you submit your ideas to friends and colleagues for criticism.

Once you get used to other people examining your ideas, start examining your own ideas yourself and practice imagining the consequences of them. In addition, you also need to develop a sense of proportion (to have a feel for how much design is appropriate for the size of the problem), a habit of fact-checking assumptions (so you don't overestimate the size of the problem), and a healthy attitude towards failure.
Finally, you must have discipline. Being aware of flaws in your plan will not make you more productive unless you can muster the willpower to correct and rebuild what you're working on.

4. Unfamiliar with the principles of security


  1. Storing exploitable information (names, card numbers, passwords, etc.) in plaintext
  2. Storing exploitable information with ineffective encryption (symmetric ciphers with the password compiled into the program; trivial passwords; any "decoder-ring", homebrew, proprietary or unproven ciphers)
  3. Programs or installations that don't limit their privileges before accepting network connections or interpreting input from untrusted sources
  4. Not performing bounds checking or input validation, especially when using unmanaged languages
  5. Constructing SQL queries by string concatenation with unvalidated or unescaped input
  6. Invoking programs named by user input
  7. Code that tries to prevent an exploit from working by searching for the exploit's signature
  8. Credit card numbers or passwords that are stored in an unsalted hash


The following only covers basic principles, but they'll avoid most of the egregious errors that can compromise an entire system. For any system that handles or stores information of value to you or its users, or that controls a valuable resource, always have a security professional review the design and implementation.
Begin by auditing your programs for code that stores input in an array or other kind of allocated memory and make sure it checks that the size of the input doesn't exceed the memory allocated for storing it. No other class of bug has caused more exploitable security holes than the buffer overflow, and to such an extent that you should seriously consider a memory-managed language when writing network programs, or anywhere security is a priority.
Next, audit for database queries that concatenate unmodified input into the body of a SQL query and switch to using parameterized queries if the platform supports it, or filter/escape all input if not. This is to prevent SQL-injection attacks.
After you've de-fanged the two most infamous classes of security bug you should continue thinking about all program input as completely untrustworthy and potentially malicious. It's important to define your program's acceptable input in the form of working validation code, and your program should reject input unless it passes validation so that you can fix exploitable holes by fixing the validation and making it more specific, rather than scanning for the signatures of known exploits.
Going further, you should always think about what operations your program needs to perform and the privileges it'll need from the host to do them before you even begin designing it, because this is the best opportunity to figure out how to write the program to use the fewest privileges possible. The principle behind this is to limit the damage that could be caused to the rest of the system if an exploitable bug was found in your code. In other words: after you've learned not to trust your input you should also learn not to trust your own programs.
The last you should learn are the basics of encryption, beginning with Kerckhoff's principle. It can be expressed as "the security should be in the key", and there are a couple of interesting points to derive from it.
The first is that you should never trust a cipher or other crypto primitive unless it is published openly and has been analyzed and tested extensively by the greater security community. There is no security in obscurity, proprietary, or newness, as far as cryptography goes. Even implementations of trusted crypto primitives can have flaws, so avoid implementations you aren't sure have been thoroughly reviewed (including your own). All new cryptosystems enter a pipeline of scrutiny that can be a decade long or more, and you want to limit yourself to the ones that come out of the end with all their known faults fixed.
The second is that if the key is weak, or stored improperly, then it's as bad as having no encryption at all. If your program needs to encrypt data, but not decrypt it, or decrypt only on rare occasions, then consider giving it only the public key of an asymmetric cipher key pair and making the decryption stage run separately with the private key secured with a good passphrase that the user must enter each time.
The more is at stake, then the more homework you need to do and the more thought you must put into the design phase of the program, all because security is the one feature that dozens, sometimes millions of uninvited people will try to break after your program has been deployed.
The vast majority of security failures traceable to code have been due to silly mistakes, most of which can be avoided by screening input, using resources conservatively, using common sense, and writing code no faster than you can think and reason about it.

1. 不断学习



2. 突发事件处理
3. 重视代码注释
4. 学会与人沟通

5. 习惯优化代码


6. 热爱分享

7. 社区意识
Deliberate practice,即刻意练习,强调的就是训练方法。如果方法不够科学,训练再久也没用。这次,我不聊具体的训练方法,而只想强调一点:Practice must be focused。如果写代码的时候不能保持专注,一直被打断的话,代码质量可能都有问题,更谈不上刻意练习,也就不能有效提高编程技能。
  • 没有外界打扰,至少不需要和同事或者用户沟通,可以更加专注;
  • 在家里,环境和心情更加放松,可以稍微健身一下,洗个澡,来点饮料和水果,再开始写代码;
  • 夜深人静,思路更加清晰,至少我是这样的;

No comments:

Post a Comment


Review (561) System Design (304) System Design - Review (196) Java (179) Coding (75) Interview-System Design (65) Interview (60) Book Notes (59) Coding - Review (59) to-do (45) Knowledge (39) Linux (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (29) Product Architecture (28) Big Data (27) Soft Skills (27) Concurrency (26) MultiThread (26) Miscs (25) Cracking Code Interview (24) Distributed (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) OOD Design (20) System Design - Practice (19) Security (17) How to Ace Interview (16) Algorithm (15) Brain Teaser (14) Redis (14) Google (13) Linux - Shell (13) Spark (13) Spring (13) Code Quality (12) How to (12) Interview-Database (12) Interview-Operating System (12) Tools (12) Architecture Principles (11) Company - LinkedIn (11) Testing (11) Resource (10) Search (10) Solr (10) Amazon (9) Cache (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Company - Uber (8) Interview - MultiThread (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Scalability (8) Cassandra (7) Company - Facebook (7) Design (7) Git (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Machine Learning (7) NoSQL (7) Trouble Shooting (7) C++ (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) API Design (4) Be Architect (4) Big Fata (4) C (4) Company Product Architecture (4) Data structures (4) Design Principles (4) Facebook (4) GeeksforGeeks (4) Generics (4) Google Interview (4) Hardware (4) JDK8 (4) Optimization (4) Product + Framework (4) Python (4) Shopping System (4) Source Code (4) Web Service (4) node.js (4) Back-of-Envelope (3) Company - Pinterest (3) Company - Twiiter (3) Company - Twitter (3) Consistent Hash (3) Elasticsearch (3) GOF (3) Game Design (3) GeoHash (3) Growth (3) Guava (3) Interview-Big Data (3) Interview-Linux (3) Interview-Network (3) Java EE Patterns (3) Javarevisited (3) Map Reduce (3) Math - Probabilities (3) Performance (3) Puzzles (3) RateLimiter (3) Resource-System Desgin (3) Scala (3) UML (3) ZooKeeper (3) geeksquiz (3) AI (2) Advanced data structures (2) AngularJS (2) Behavior Question (2) Bugs (2) Coding Interview (2) Company - Netflix (2) Crawler (2) Cross Data Center (2) Data Structure Design (2) Database-Shard (2) Debugging (2) Docker (2) Garbage Collection (2) Go (2) Hadoop (2) Html (2) Interview - Soft Skills (2) Interview-Miscs (2) Interview-Web (2) JDK (2) Logging (2) POI (2) Papers (2) Programming (2) Project Practice (2) Random (2) Software Desgin (2) System Design - Feed (2) Thread Synchronization (2) Video (2) reddit (2) Ads (1) Algorithm - Review (1) Android (1) Approximate Algorithms (1) Base X (1) Bash (1) Books (1) C# (1) CSS (1) Chrome (1) Client-Side (1) Cloud (1) CodingHorror (1) Company - Yelp (1) Counter (1) DSL (1) Dead Lock (1) Difficult Puzzles (1) Distributed ALgorithm (1) Eclipse (1) Facebook Interview (1) Function Design (1) Functional (1) GoLang (1) How to Solve Problems (1) ID Generation (1) IO (1) Important (1) Internals (1) Interview - Dropbox (1) Interview - Project Experience (1) Interview Stories (1) Interview Tips (1) Interview-Brain Teaser (1) Interview-How (1) Interview-Mics (1) Interview-Process (1) Java Review (1) Jeff Dean (1) Joda (1) LeetCode - Review (1) Library (1) LinkedIn (1) LintCode (1) Mac (1) Micro-Services (1) Mini System (1) MySQL (1) Nigix (1) NonBlock (1) Process (1) Productivity (1) Program Output (1) Programcreek (1) Quora (1) RPC (1) Raft (1) Reactive (1) Reading (1) Reading Code (1) Refactoring (1) Resource-Java (1) Resource-System Design (1) Resume (1) SQL (1) Sampling (1) Shuffle (1) Slide Window (1) Spotify (1) Stability (1) Storm (1) Summary (1) System Design - TODO (1) Tic Tac Toe (1) Time Management (1) Web Tools (1) algolist (1) corejavainterviewquestions (1) martin fowler (1) mitbbs (1)

Popular Posts