《程序员修炼之道-从小工到专家》读书笔记


提示1:Care About Your Craft

关心你的技艺

提示2:Think!About Your Work

思考!你的工作


提示3:Provide Options,Don’t MakeLame Excuses
提供各种选择, 不要找蹩脚的借口

第一章、注重实效的哲学
1、我的源码让猫给吃了
注重实效的程序员对他或她自己的职业生涯负责,并且不害怕承认无知和错误。

负责
责任是你主动承担的东西,你承诺确保某件事情正确完成,但你不一定能直接控制事情的每一个方面。出了尽你所能之外,你必须分析风险是否超出了你的控制,对于不可能做到的事情或是风险太大的事情,你有权不去为之负责。你必须基于你自己的道德准则和判断来做出决定。

3、石头汤与煮青蛙
设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊

5、你的知识资产
提示8:Invest Regularly in YourKnowledge Portfolio

定期为你的知识资产投资

目标

·每年至少学习一种新语言

·每季度阅读一本技术书籍

·也要阅读非技术书籍

·跟上潮流

提示9:Critically Analyze What YouRead and Hear

批判地分析你读到的和写到的

6、交流
提示10:It’s Both What You Say and the Way You Say it

你说什么和你怎么说同样重要


·知道你想要说什么

·了解你的听众

·选择时机

·选择风格

·让文档美观

·让观众参与

·做倾听者

·回复他人

第二章 注重实效的途径
7 重复的危害

提示11: DRY dont repeat yourself
不要重复你自己

提示12:make it easy to reuse

让复用变得容易

8 正交性
提示13:Eliminate Effects BetweenUnrelated Things

编码

让你的代码保持解耦 不会没有必要地向其他模块暴露任何事情、也不依赖其他模块的实现的模块

避免使用全局数据

避免编写相似的函数

9可撤销性
提示14:there are no final decisions

不存在最终决策

要把决策视为是写在沙滩上的,而不要把它们刻在石头上,大浪随时可能到来,把他们抹去

10曳光弹
提示15:Use Tracer Bullets to Findthe Target

用曳光弹找到目标

曳光弹告诉你击中的是什么

曳光代码

构建从未构建过的东西时,与真正的子弹在相同的未知环境下工作,快速飞向目标得到即时反馈。

为了在代码中获得同样的效果,我们要找到某种东西,让我们能快速、直观和可重复地从需求出发,满足最终系统的各个方面的要求。

曳光开发与项目永不会结束的理念是一致的:总有改动需要完成,总有功能需要增加。

11 原型与便签
提示16:Prototype to Learn

为了学习而制作原型

构建原型的原因:为了分析和揭示风险,并以大大降低的代价、为修正提供机会。

如果发现自己处在不能放弃细节的环境中,就要问自己是否真的在构造原型,获取曳光弹方法更适合这种情况。

原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。

怎样使用原型?

构建原型时,可以忽略:正确性、完整性、健壮性、风格。

12 领域语言
提示17:Program Close to the Problemdomain

靠近问题领域编程

考虑让你的项目更靠近问题领域,通过在更高的抽象层上编码,你获得了专心解决领域问题的自由,并且可以忽略琐碎的实现细节。

13估算
提示18:Estimate to Avoid Surprises

估算,以避免发生意外

关于估算,你使用的单位会对结果的解读造成影响


估算来自哪里

理解提问内容-建立系统的模型-把模型分解为组件-给每个参数指定值-计算答案-追踪你的估算能力

估算项目进度

为项目确定进度表的唯一途径常常是在相同的项目上获取经验

检查需求-分析风险-设计、实现、集成-向用户确认

提示19 iterate the Scedule with the Code

通过代码对进度表进行迭代

第三章 基本工具
14纯文本的威力
提示20:keep knowledge in Paint Text用纯文本保存知识

15 shell游戏
GUI的好处是WYSIWYG所见即所得(what you see is what you get)。缺点是WYSIAYG所见即全部所得(what you see is all you get)

提示21:Use the Power of CommandShells ?

利用命令shell的力量

16强力编辑
提示22:Use a Single Editorr Well

用好一种编辑器

选一种编辑器,彻底了解它,并将其用于所有的编辑任务

17源码控制
提示23:Always Use Source CodeControl

总是使用源码控制

18调试
提示24:Fix the Problem,Not theBlame

要修正问题,而不是发出指责

bug是你的过错还是别人的过错,并不是真的很有关系。它仍然是你的问题

调试的第一准则:

提示25:Don't Panic 不要慌

如果你目睹bug或见到bug报告时的第一反应是“那不可能”你就完全错了,一个脑细胞都不要浪费在“但那不可能发生”起头的思路上,因为很明显,那不仅可能,而且已经发生了

在调试时小心“近视”。要抵制只修正你看到的症状的迫切愿望:更有可能的情况是,实际的故障离你正在观察的地方可能还有几步远,并且可能涉及许多其他的相关事物。要总是设法找出问题的根源,而不只是问题的特定表现。

从何处开始?

在开始查看bug之前,要确保你是在能够成功编译的代码上工作-没有警告,把时间浪费在设法找出编译器能够为你找出的问题上没有意义

你也许需要与报告bug的用户面谈,以搜集比最初给你的数据更多的数据

人工合成的测试不能足够地演练应用,你必须强硬地测试边界条件,又测试现实中的最终用户的使用模式。

测试策略?

开始修正bug的最佳途径是让其可再现。

橡皮鸭

找到问题的一种非常简单、却又特别有用的技术是向别人解释它。

消除过程

bug有可能存在于OS、编译器、或是第三方产品中--但这并不应该是你的第一想法。有大的多的可能性的是,bug存在于正在开发的应用代码中。与假定库本身出了问题相比,假定应用代码对库的调用不正确通常更有好处。

如果你只改动了一样东西,系统就停止了工作,那样东西很可能就需要对此负责-直接或间接地,不管那看起来有多牵强。

提示26:“Select”Isn’t Broken

“select”没有问题

造成惊讶的要素

某样东西出错,你感到吃惊的程度与你对正在运行的代码的信任及信心成正比。在面对“让人吃惊”的故障时,你必须意识到你的一个或更多的假设是错误的。不要因为你知道它能工作而轻易放过与bug有牵连的例程或代码。证明它,用这些数据、这些边界条件、在这个语境中证明它

提示27 Don't Assume it - Prove it

不要假定,要证明

当你遇到让人吃惊的bug时,出了只是修正他以外,你还需要确定先前为什么没有找出这个bug。考虑是否需要改进单元测试或其他测试。

如果bug是一些坏数据的结果,在这些数据造成爆发之前传播通过了若干层面,看一看在这些历程中进行更好的参数检查是否能更早地隔离它。?

代码中是否有其他地方容易受这同一个bug的影响。

如果bug是某人错误假定的结果,与整个团队一起讨论这个问题,如果一个人有误解,那么许多人也会有。

调试检查列表

正在报告的问题是底层bug的直接结果,还是只是症状?

bug真的在编译器里?在OS里?还是在你的代码里?

如果你向同事详细解释这个问题,你会说什么?

如果可疑代码通过了单元测试,测试是否足够完整,如果你用该数据进行单元测试,会发生什么?

造成这个bug的条件是否存在于系统中的任何其他地方?

19文本操纵
提示28:Learn a Text ManipulationLanguage

学习一种文本操纵语言

20代码生成器
提示29:Write Code That Writes Code

编写能编写代码的代码

第四章 注重实效的偏执
提示30 You Can't Write Perfect Software

你不可能写出完美的代码

注重实效的程序员连自己也不信任。知道没有人能编写出完美的代码,包括自己,所以注重实效的程序员针对自己的错误进行防卫性的编码。

21按合约设计
DBC

软件设计中为了确保软件模块的权利与责任,以确保程序的正确性,也引入了合约的概念,这就是DBC(Design By Contract)按合约设计。

DBC的方式:用文档记载并约定软件模块的权利和责任,以确保程序正确性。用文档记载这样的声明,并进行校验,是按合约设计(DBC)的核心所在。

DBC的流程:

     1.前条件:为了调用例程,必须为真的条件

     2.后条件:例程保证会做的事情

     3.类不变项:类确保从调用者的视角来看,该条件总是为真

提示31:Design with Contracts

通过合约进行设计

实现DBC

 1.断言

    支持断言式编程的语言可以通过断言部分实现DBC。

    为什么是部分实现?因为:

      首先,断言不支持继承层次向下遗传

      其次,断言不支持“老”值。(什么是“老”值?)

      最后,runtime和库的设计不支持合约(什么意思?)

  2.语言支持

    有些语言内建对DBC的支持(Eiffel和Sather)

    C,C++的有些预处理器能处理作为特殊注释嵌入在代码中的DBC。预处理器可以把这些注释展开为断言代码(Nana)

    Java可以使用iContract

22死程序不说谎
提示32 Crash Early

早崩溃

要奔溃,不要破坏

尽早检测问题的好处之一是你可以更早崩溃,而有许多时候,让你的程序崩溃是你的最佳选择。

死程序带来的危害通常比有疾患的程序要小得多。

23断言式编程
提示33:if it can't happen, useassertions to ensure that it won't 如果它不会发生,用断言确保它不会发生

绝不要把必须执行的代码放在assert中

不要用断言代替真正的错误处理,断言检查的是绝不应该发生的事,提供给你的assert宏会在断言失败时调用exit。

让断言开着

你的第一条防线是检查任何可能的错误,第二条防线是使用断言设法检测你疏漏的错误

24何时使用异常
检测程序每一个可能的错误,特别是意料之外的错误,是一种良好的实践,但是在实践中这可能会把我们引向相当丑陋的代码,此时使用异常会使程序更加清晰。

什么是异常情况

如果你打开一个文件而该文件不存在,应该触发异常吗?如果文件应该在那里,那么触发异常就有正当理由,如果你不清楚文件是否应该在那里,那么久不应该触发异常

提示34:Use Exceptions For ExceptionProblem

将异常用于异常的问题

异常表示即时的、非局部的控制转移--这是一种级联的goto。

错误处理器是另一种选择。

不支持异常的语言常常拥有一些其他的非局部控制转移机制(c拥有longjmp/setjmp)。

25怎样配平资源
资源使用遵循一种可预测的模式:你分配资源,使用它,然后解除其分配

提示35:Finish what you Start 要有始有终

嵌套的分配

1、以与资源分配的次序相反的次序解除资源的分配,这样如果一个资源含有对另一个资源的引用,你就不会造成资源被遗弃

2、在代码的不同地方分配同一组资源时,总是以相同的次序分配他们,这样降低发生死锁的可能性。不管我们使用的是各自资源--事务、内存、文件、线程、窗口--基本的模式都适用

第五章 弯曲,或折断
26解耦与德墨忒尔法则
把你的代码组织成最小组织单位(模块),并限制它们之间的交互。如果随后处于折中必须替换某个模块,其他模块仍能正常工作

提示36:Minimize Coupling BetweenModules

使模块之间的耦合减至最小

德墨忒尔法则:

得墨忒耳定律--对象 O 的 M 方法,可以访问/调用如下的:

1.对象 O 本身

2.M 方法的传入参数

3.M 方法中创建或实例化的任意对象

4.对象 O 直接的组件对象

5.在M范围内,可被O访问的全局变量

27.元程序设计
提示37:Configure,Don't Integrate 要配置,不要集成

元数据:元数据是关于数据的数据,最常见的例子可能是数据库schema或数据词典。元数据是任何对应用进行描述的数据---应用应该怎样运行、它应该使用什么资源,等

提示38:Put Abstractions in Code, Details in Metadata

将抽象放进代码,细节放进元数据

这样做的好处:

·它迫使你解除你的设计的耦合,从而带来更灵活、可适应性更好的程序。

·它迫使你通过推迟细节处理,创建更健壮、更抽象的设计----完全推迟到程序之外。

·无需重新编译应用,你就可以对其进行定制。还可以通过这一层面的定制,轻松地绕开正在运行产品中的重大bug

28时间耦合
时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在时间重的相对位置)

为并发进行设计

首先,必须对任何全局或静态变量加以保护,使其免于并发访问

此外,不管调用的次序是什么,你都需要确保你给出的是一致的状态信息,必须确保,对象在任何可能被调用的时刻,都处在有效的状态中。

第六章 当你编码时
只要你在制作代码,你就要记住,有一天你必须对其进行测试,要让代码易于测试,要让代码易于测试,这样你将增加它实际通过测试的可能性。

31靠巧合编程
提示44:don't program by coincidence不要靠巧合编程

怎样深思熟虑地编程

·总是意识到你在做什么。

·不要盲目地编程。试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导。

·按照计划行事,不管计划是在你的头脑中,还是在哪里

·依靠可靠的事物,不要依靠巧合或假定。如果你无法说出各种特定情形的区别,就假定是最坏的。

·为你的假定建立文档

·不要只是测试你的代码,还要测试你的假定。不要猜测,要实际尝试它,编写断言测试你的假定

·为你的工作划分优先级。把时间花在最重要的方面,很有可能,它们是最难的部分。

·不要做历史的奴隶,不要让已有的代码支配将来的代码。如果不适用,所有的代码都可被替换。即使是一个程序中,也不要让你已经做完的事情约束你下一步要做的事情--准备好进行重构

32 算法速率
注重实效的程序员几乎每天都要使用:估计算法使用的资源--时间、处理器、内存、等。

大多数算法都不是线性的。

大多数算法都是亚线性的,二分查找在查找匹配项时无需查看每一个候选项。

有些算法比线性情况要糟得多,其运行时间或内存需求的增长要远远快于n。处理10个数据项需要1分钟的算法要处理100个数据项可能要一生的时间。

O()表示法:O()表示法是处理近似计算的一种数学途径。当我们写下某个特性的排序历程对n个记录进行排序所需的时间是O(n2)时,我们的意思是,在最坏的情况下,所需时间随n的平方变化。使记录数加倍,时间大约将增加4倍吧O视为“阶为。。。”O()表示法对我们在度量的事物(时间、内存等)的值设置了上限。如果我们说某函数需要O(n2)时间,那么我们就知道他所需时间的上限不会比n2增长得更快。有时我们会遇到相当复杂的O()函数,但是因为随着n的增加,最高阶项将主宰函数的值,习惯做法是去掉所有低阶项,并且对任何常数系数都不予考虑。O(n2/2+3n)和O(n2/2)一样,后者又与O(n2)等价。这实际上是O(n)表示法的一个弱点,某个O(n2)算法可能比另一个O(n2)算法快1000倍,但是你从表示法上却看不出来。

O(1) ??????常量型(访问数组元素,简单语句)

O(lg(n)) ???对数型(二分查找) lg(n)是log2(n)的简写形式

O(n) ??????线性型(顺序查找)

O(nlg(n)) ???比线性差,但是不会差很多(快速排序,堆排序的平均运行时间)

O(n2) ??????平方率型(选择和插入排序)

O(n3) ??????立方型(2n*n矩阵相乘)

O(Cn) ??????指数型(旅行商问题,集合划分)

常识估算

简单循环:如果某个简单循环从1运行到n,那么算法很可能就是O(n)--时间随n线性增加。例子有穷举查找、找到数组中最大值等。

嵌套循环:如果要在循环中嵌套另外的循环,那么你的算法就变成了O(m*n),这通常发生在简单的排序算法如冒泡排序。

二分法:如果你的算法每次循环时把事物的集合一分为二,那么它很可能是对数型O(lg(n))。对有序列表的二分查找、遍历二叉树。

分而治之:划分其输入,并独立地在两个部分上进行处理,然后再把集合组合起来的算法可能是O(nln(n)),经典例子是快速排序,其工作方式是:把数据划分成两半,并递归地对每一半进行排序。尽管在技术上是O(n2),但因为其行为在馈入的是排过序的输入时会退化。

组合:只要算法考虑事物的排列,其运行时间就有可能失去控制,这是因为排列涉及到阶乘。

实践中的算法速率:无论何时你发现自己在编写一个简单的循环,你都知道你有一个O(n)算法。如果循环内含有循环,那么就是O(m*n)算法,你应该问问自己,这些值可能有多大,如果数值有限,你就知道代码大概要运行多长时间,如果数值取决于外部因素,那么你可能会想要停下来,考虑一下大量数据对运行时间或内存消耗可能带来的影响。

提示45:Estimate the Order of Your Algorithms 估算你的算法的阶

提示46:Test Your Estimates 测试你的估算

33重构
重写、重做和重新架构代码合起来,称为重构。

你应该在何时进行重构

当你遇到绊脚石--代码不再合适,你注意到有两样东西其实应该合并或是其他对你来说是“错误”的东西--不要对改动犹豫不决,应该现在就做,当代码有以下特性:

·重复--你发现了对DRY原则的违反(重复的危害)

·非正交的设计--你发现有些代码的设计可以变得正交(正交性)

·过时的知识--事情变了,需求转移了,你对问题的了解加深了

·性能--为了改善性能

提示47:Refactor Early,Refactor Often 早重构,常重构

怎样进行重构

就核心而言,重构就是重新设计

1、不要试图在重构的同时增加功能

2、在开始重构之前,确保你拥有良好的测试。尽可能经常运行这些测试。

3、采取短小、深思熟虑的步骤:把某个字段从一个类移往另一个类,把两个类似的方法融合进超类中。

下次你看到不怎么合理的代码时,既要修正他,也要修正依赖于它的每样东西,要管理痛苦:如果他现在有损害,但以后的损害会更大。(不要容忍破窗户)

34易于测试的代码
我们需要在一开始把可测试性构建进软件中,并且在把各个部分连接在一起之前对每个部分进行彻底的测试。

软件的单元测试是对模块进行演练的代码,在典型情况下,单元测试将建立某种人工环境,然后调用被测模块中的例程,然后根据已知值对返回结果进行检查

提示48:Design to Test:为测试而设计

编写单元测试

所有的main历程都可以用于运行单元测试,当应用自身运行时它将会被忽略,这样做的好处是,你交付的代码仍然含有测试,可用于在现场对问题进行诊断

在c++中,通过使用#ifdef有选择地编译单元测试

使用测试装备

不管你决定采用的技术是什么,测试装备都应该有以下功能:

·用于指定测试与清理的标准途径

·用于选择个别或所有可用测试的方法

·分析输出是否是预期(或意外)结果的手段

·标准化的故障报告形式

测试应该是可组合的,也就是说,测试可以由子组件的子测试组合到任意深度

第七章 在项目开始之前

36 需求之坑
完美,不是在没有什么需要增加,而是在没有什么需要去掉时达到的

提示51:Don't GatherRequirement--Dig for Them

不要搜集需求--挖掘它们

挖掘需求

找出用户为何要做特定时期的原因、而不只是他们目前做这件事情的方式,这很重要,到最后,你的开发必须解决他们的商业问题,而不只是满足他们陈述的需求。

有一种能深入了解用户需求,却未得到足够利用的技术:成为用户。

提示52:Work with a User to ThinkLike a User

与用户一同工作,以像用户一样思考

看远些

Abstractions Live Longer than Detail 抽象比细节活得更长久

管理需求增长得关键是向项目出资人指出每项新特性对项目进度的影响

37解开不可能解开的谜题
提示55 Dont Think Outside the Box,- Find the Box

不要再盒子外思考--要找到盒子

38等你准备好
提示56 Listen to Magging Doubts - Start When You're Ready

倾听反复出现的疑虑,等你准备好再开始

当你面对一件任务时,如果你反复感到疑虑,或是体验到某种勉强,要注意它。你可能无法指出问题所在,但给他时间,你的疑虑就会结晶成某种你可以理解的东西。软件开发仍然不是科学,让你的直觉为你的表演做出贡献。

39规范的陷阱
认为规范将捕捉系统或其需求的每一处细节和细微差别,这很幼稚

语言自身的表达能力存在着问题。

提示57:Some Things Are Better Donethan Described

对有些事情,“做”胜于“描述”

作为注重实效的程序员,你应该倾向于把需求收集、设计、以及实现视为同一个过程----教付高质量的的系统---的不同方面。不要信任这样的环境:搜集需求、编写规范、然后开始编码,所有这些步骤都是孤立进行的。

相反,要采用无缝的方法。健康的开发过程孤立吧来自实现与测试的意见反馈到规范中。

40圆圈与箭头
盲目地采用任何技术,而不把它放进你的开发实践和能力的语境中,这样的处方肯定会让你失望。

提示58:Don't be ?a Slave toFormal Methods不要做形式方法的奴隶

注重实效的程序员批判地看待方法学,并从各种方法学中提取精华,融合成每个月都在变得更好的一套工作习惯。你应该不断努力提炼和改善你的开发过程,不要把方法学的呆板限制当做你的世界的边界。

提示59:Expensive Tools Do NotProduce Better Designs

昂贵的工具不一定能制作出更好的设计。

第八章 注重实效的项目

41注重实效的团队
·不要留破窗户

·团队作为一个整体,不应该容忍破窗户--那些小小的、无人修改的不完美。

·煮青蛙

·确保每一个人都主动地监视环境的变化

·交流

·不要重复你自己

·正交性

认为项目的各种活动--分析、设计、编码、测试--会孤立地发生,这是一个错误。它们不会孤立地发生,它们是看待同一问题的不同方式。

提示60:Orgnaize AroundFunctionality, Not Job Functions

围绕功能,而不是工作职务进行组织。

项目至少需要两个“头”--一个主管技术,一个主管行政,技术主管设定开发哲学和风格,给各团队指派责任,并仲裁成员间不可避免的“讨论”。行政主管调度个团队所需的各种资源,监视并报告进展情况,并根据商业需要帮助确定各种优先级。

42无处不在的自动化
提示61:Don't Use Manual Procedure 不要使用手工流程

1、shell脚本

2、crontab

3、makefile

让计算机去做重复、庸常的事-它会比我们做得更好。

43 无情的测试
提示62:Test Early, Test Often, TestAutomatically

早测试、常测试、自动测试

bug被发现得越早,进行修补的成本就越低。“编一点,测一点”是Smaltalk世界里流行的一句话

提示63:Coding Ain't Done Till AllThe Test Run

要到通过全部测试,编码才算完成

单元测试:对某个模块进行演练的代码

集成测试:说明组成项目的主要子系统能工作、并且能很好地协同

验证和校验:

资源耗尽、错误及恢复:代码可能遇到的一些限制包括:内存空间、磁盘空间、CPU带宽、挂钟时间、磁盘带宽、网络带宽、调色板、视频分辨率

性能测试:软件是否能满足现实世界的条件下的性能需求--预期的用户数、连接数、或每秒事务数。它可以伸缩吗。

可用性测试:根据人的因素考察可用性

测试数据:你需要大量数据、你需要能强调边界条件的数据、你需要能展现特定统计属性的数据

对测试进行测试:当你编写了一个测试、用以检测特定的bug时,要故意引发bug,并确定测试会发出提示

提示64:Use Saboteurs to Test YourTesting ?

通过“蓄意破坏”测试你的测试

提示65:Test State Coverage,Not Code Coverage

测试状态覆盖,而不是代码覆盖。

提示66:Find Bugs Once

一个bug只抓一次

一旦测试人员找到了某个bug,这应该是测试人员最后一次发现这个bug。应该对自动化测试进行修改,从此每次都检查那个特定的bug。

全都是写

注重实效的程序员汇报文档当做整个开发过程的完整组成部分加以接受,如果可能,就放在代码本身当中,

提示67:Treat English as JustAnother Programming Language

把英语当做一种编程语言

所有的文档都是代码的反映

提示68:Build Documentation In,Don't Bolt It On.

把文档建在里面,不要栓在外面。

44.代码中的注释:
一般而言,注释应该讨论为何要做某事、它的目的和目标。代码已经说明了它是怎样完成的,所以再为此加上注释是多余的-而且违反了DRY原则。

不应该出现在源码注释中的一些内容:

·文件中的代码导出的函数的列表

·修订历史

·该文件使用的其他文件的列表

·文件名

在源文件里应该出现的最重要的信息之一是作者的姓名--不一定是最后编辑文件的人,而是文件的所有者,使责任和义务与源码联系起来,能够奇迹地使人保持诚实‘

45.极大的期望
在现实中,项目的成功是由它在多大程度上满足了用户的期望来衡量的。不符合用户预期的项目注定是失败的,不管交付的产品在绝对的意义上有多好。

提示69:Gently Exceed Your Uses'Expectations

温和地超出用户的期望

交流期望

随着对用户需求理解的发展,你会发现他们有些期望无法满足,有些期望过于保守,你的部分角色就是要就此进行交流

我们的角色不是控制用户的希望,而是要与他们一同工作,达成对开发过程和最终产品、以及他们尚未描述出来的期望的共同理解

额外的一英里

给用户的东西要比他们期望的多一点,给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。

46傲慢与偏见
提示70:Sign Your Work 在你的作品上签名

过去时代的手艺人为能在他们的作品上签名而自豪。你也应该如此

不应该怀着猜忌心阻止要查看你代码的人;出于同样的原因,你也该带着尊重对待他人的代码

我们想要看到对所有权的自豪。“这是我编写的,我对自己的工作负责”,你的签名该被视为质量的保证。

发布了20 篇原创文章 · 获赞 1 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhjixi495/article/details/103195443