Thursday, April 21, 2016

How to Be a Better Programmer - Part 3



https://mp.weixin.qq.com/s/W1PBOScBCxbHfoyccg5tGw
$ 程序员的高傲:提交的代码被任何人读起来都应该是优雅的;交付给测试的功能点一定是自己反复验证过的,出现低级BUG丢不起那人;敬畏生产环境,上线脚本/配置/变量/步骤/困难全都罗列的一清二楚,上线失败如同过掉门将面对空门将球打飞,可笑又可耻。

$ 装逼套路:能用命令行绝不用图形界面,能用快捷键绝不用鼠标,IDE必须黑色主题,桌面墙纸必须个性,案头必备一本英文原版书(就算垫显示器),必须盲敲数字键(别笑,我不认为现在程序员有多少可以做得到或者做得6)
总之,在编程起步的这段模仿路上,从例行工作到三余读书,从照搬仿效到习惯养成,从实际行为(形)到思想境界(神),其实你会发现这模仿本身也是个成长的过程,只是这路不是你自己摸索的而是踩着前人的脚印。您再看,这个过程缘何我模仿的自得其乐?因为我把自己视作孩童,放低身段,自降一维,只求成长。


https://zhuanlan.zhihu.com/p/34293012
因为他们只有第一年的时候在学习如何适应工作,如何提高工作效率,如何与同事沟通。以至于后面的九年都在用第一年积累的经验以及所学习到的技能。
许岑老师说:成年人是靠任务驱动的。




什么叫任务驱动?就是我干这件事是有我要解决的问题的,我是奔着我的目标去的。现在很多 App 很多产品都提倡利用你的碎片化时间,所以你就扑腾扑腾不断跑来跑去捡了很多的树枝,后来发现自己的院子里堆满了树枝,而想象中的参天大树毫无踪迹。碎片化时间的基础是,你要有自己强壮的主干,慢慢去吸收阳光吸收养料,碎片化地成长树叶和树枝,这样子你才有成为参天大树的可能,否则就只是在院子里堆满树枝了。





以我自己为例子,我从来没觉得我好像很喜欢大数据,机器学习,知识图谱,分布式系统这些单个的技术,我只是培养了自己学习的能力。我几乎能在一两周的时间内熟悉一个小东西并且搭一个应用把它利用起来,然后把它讲给我的小伙伴们听,如果我的小伙伴们听懂了,他们也可以去创造自己的应用了,这个时候的我对于这项技术也基本都掌握了。所以我的目标是很明确的,我学习这项技术第一是为了完成我手头的事情,第二是为了把它分享给我的小伙伴们,这样子下来,我就有了充足的任务去学习了。

你说你不知道怎么做,我可以告诉你。首先做好你手头的事情,让它们更优质更快速更有效率完成,然后帮助你的团队伙伴也跟你一样。做到这两样我保证你会有非常不同的感受。



这样做了以后呢,同时保持你的大脑是足够开放的,遇到负反馈就改进,遇到正反馈就要思考是不是真的,能够接受所有的信息。大脑进化了这么久,很喜欢听到赞美,不喜欢看到反对的意见,这是潜意识里基因里决定的,不是大脑能决定的,但是我们人类最大的优势就是我们可以决定不顺从它。


http://www.yacoset.com/Hoe/signs-that-you-re-a-good-programmer
http://blog.jobbole.com/87799/

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.

Symptoms

  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.

Symptoms

  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

Remedies

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.

5.难以看透递归

递归的思想很容易理解,但程序猿们经常在自己脑子里想象一次递归操作的结果时遇到困难,或想不通一个简单函数是怎么计算出复杂结果的。这些不解使得要设计一个递归函数变得难上加难,因为当你要对初始条件或递归调用的参数进行测试时,你想象不出“当前走到哪一步了”。
特征
  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

Symptoms

  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

Remedies

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. 不断学习
程序员的学习从Google开始。一遇到问题就求助于同事的程序员不仅会让正在紧张工作的同事感到反感而且还易养成依赖的坏毛病。正确的解惑姿势应该是擅用身边最大的资源器——搜索引擎,它检索的出来的信息远比你周围的同事能提供给你的多得多。

不断接受新的技术。程序员是一个非常残忍的职业。你所学所用的语言、框架、模式,很可能在数年内就成昨日黄花了。所以除了技术的深度之外,想成为优秀程序员的你还需要重视技术的广度,C语言、Java、PHP、C++、Perl….总之,越大越好。

阅读大量别人的代码。提升编程技能的一个鲜为人知的方法就是阅读大量别人编写的代码,吐槽的同时边阅读边思考:如果换成我应该如何编写这部分代码?这段代码是否还有可优化的空间?如何优化?这项技术如何应用在我过去已经编写的代码中?

2. 突发事件处理
斑斑一向认为如何处理bug、崩溃、调优、入侵等突发事件是比编程本身更能考验程序员能力的,也是一般程序员和优秀程序员的根本差距所在。当面对一个未知的问题时,你如何抽丝剥茧地分析问题的潜在原因、如何一点一点的排除干扰项找到问题的本源、如何选择最合适的解决方案,都是衡量一个程序员是否优秀的重要标准。一个优秀的程序员会用尽一切方法让事情前进。
3. 重视代码注释
如果你看自己三个月前写的代码,也许你能够凭借自己写代码的习惯和记忆中这段代码的运行结果而想明白当初自己为什么要这么写,但如果你看的是自己三年前写的代码,恐怕就没有这么好的运气了,这个时候你就需要注释来帮忙了。优秀的程序员不止代码是优秀的,注释也同样。简洁的代码配上详尽的注释,完美的简直不要不要的。
4. 学会与人沟通
沟通无处不在,而且不可避免,一个优秀程序员的沟通绝不是简单的和策划、产品经理讨价还价,而是善于运用沟通的技巧更精确的表达出自己的立场。斑斑一直认为沟通也是编写代码的一部分,沟通出问题势必会影响代码的编写质量,使最终的呈现结果出现瑕疵。立志成为优秀程序员的你绝不会被区区沟通所打败,不是吗


5. 习惯优化代码

“代码能跑”可不是优秀程序员编程的终极目标,编程人员工作的第一步就是编写出符合要求的代码,但这也仅仅只是第一步而已。作为一名优秀的的程序员,你需要经常性地进行代码优化,把自己编写的代码当成是一件艺术品,精益求精。如果真的有让你忍无可忍的代码,麻烦请重构。

6. 热爱分享

7. 社区意识

https://blog.fundebug.com/2018/01/17/coding-in-the-night
Deliberate practice,即刻意练习,强调的就是训练方法。如果方法不够科学,训练再久也没用。这次,我不聊具体的训练方法,而只想强调一点:Practice must be focused。如果写代码的时候不能保持专注,一直被打断的话,代码质量可能都有问题,更谈不上刻意练习,也就不能有效提高编程技能。
于是,我做了一个艰难的决定:晚上写代码。做决定往往比做事情更痛苦,这次也不例外,这事比想象中有意思多了:
  • 没有外界打扰,至少不需要和同事或者用户沟通,可以更加专注;
  • 在家里,环境和心情更加放松,可以稍微健身一下,洗个澡,来点饮料和水果,再开始写代码;
  • 夜深人静,思路更加清晰,至少我是这样的;
现在,我每天晚上大概会写1~2个小时代码,工作量不会太多,但是对代码质量的要求会更高。对我而言,晚上写代码更多的是练习编程技能,而不是单纯为了完成工作任务。这样,我会花更多时间去思考:如何写出更好的代码?如何提高编程效率?哪些是我的知识盲点?至少我会写更加全面的单元测试
我挺喜欢写代码的,因此每天可以多写点代码的话,感觉还是很不错的。白天工作的时候,也可以更加从容一些,不再因为写代码的事而焦虑。
并不一定非得晚上写代码,重要的是你应该在找到合适的时间段,专注地练习,这样才能真正有所提高。


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