转载自 # 系统管理员的 18 个基本准则

靠规则而活

不是仅仅只要知道怎么建立维护服务器和理解系统命令是怎么工作的就可以让你成为一个好的系统管理员——甚至也也不是知道当系统宕掉时怎么去修复,怎么去监控系能,怎么去管理备份或者怎么可以写出漂亮的脚本。而是除此之外还要为自己制定一套能让系统良好运行以及让你用户高兴的规则。

可能其中很多你已经听过无数次,也可能有些是你遇到问题时吸取的经验教训。这些规则在过去几十年的系统管理中都已经证明了它们的价值并且能够帮助我们在脑子发热时冷静下来。

禁止做任何不能回滚的操作

除了一些最简单的修改,你应该备有回滚方案。当你进行改动时想好了回滚方案吗?有很多办法可以在修改的途径中留下技术上的“面包屑”,这能够让你随时回到你开始的原点。备份你需要编辑的文件,可能是你记不住的一些复杂的配置文件。记录下你遇到的问题。在进行生产环境之前先在测试环境下测试,在你继续之前确保所有的修改都是合理的。

提前计划好需要改动的地方,最好能够采取同行评审的方式。另外一双眼睛可能可以看到你没有考虑到的问题。

避免在周五做任何改动

不要在你将要不在的几天之前做任何修改。确保在不需要干预的情况下这些修改也可以在系统上正常运行。

弄清楚根本原因

去挖掘你所碰到问题的根本原因。当怀疑的时候,用“五个为什么”原则。我的服务器宕了,为什么?是因为内存不够。为什么不够?因为其中一个进程“疯了”。为什么“疯了”?因为它进入到循环中。为什么进入?因为配置文件中有错误。为什么有错误?因为我在周五晚上离开之前修改了文件,但是忘了测试确保所有一切都可以正常工作。

实践灾备方案

实践灾备方案在必要的时候能够顺手使用。如果你不实践,有两种可能会发生。第一,你没有信心你的方案是否有用,第二,你可能会不确定你所要采取的措施。比如说,你需要迁移数据库到一个远程站点。你是否知道你要运行的命令?准备好数据备份了吗?还是需要去创建备份?你是否知道迁移文件需要多久?你是否会准备好远程启动数据库?你是否有一套测试来检验其能够很好地运行?

不要去依赖没有完整测试过的脚本

这很容易犯错的。即使你已经写了几十年的脚本也要去测试,特别是可能别人在某天会运行这些脚本。带参数测试和不带参数测试。模拟其他人可能会犯的错误。总之,必须要测试脚本。

三次以上的重复和复杂的操作,必须自动化

在别名、函数和脚本中使用你最熟练的命令并且赋予它们有意义的命名。将那些复杂的过程写进到脚本中,你就无需每次都要去想那些必要的步骤和复杂的命令。这样你会在费时费力的工作中节省很多的时间和精力,并且在需要其他人为你工作时,让你有更多的轻松时间。

伯乐在线推荐阅读:《超过 90 秒的任务不自动化,你好意思说自己是黑客?

为你的工作建立文档

用文档记录下你的日常工作。你做的事里面有哪些对别人来说不好理解?可能你要跑个脚本在日志文件中查找磁盘吃紧或者数据中心湿度太高的警告。

往脚本中添加注释。你可能会觉得你用的那些命令很显而易见,但是当你隔了一两年没用再回去用的时候就不会那么显而易见了。不要为了简洁而牺牲了可读性。别人可能会要读你的代码。完整地写下你所做的一切,别人能够在你决定跳槽时候很好地接手你的工作。

重视你犯的错误

用你自己的方式去理解错误是避免再犯的唯一方法。重视你所做错的并且注意那些多次犯错的类型

可能是你忘了修改默认密码从而密码过期导致启动的服务宕了;可能你没有去验证备份是否可用;也可能是当其他人离开公司时你忘了封锁他们的账号。不管什么问题,需要注意记录你的疏忽并且找到一种可靠的方式来提醒自己容易忘记的事。

强迫自己轻微妄想症

经常问自己问题,比如“别人会不会误用这个呢?”,“别人会不会弄坏这个呢?”,以及“这个服务会怎么收到攻击?”给所有的脚本设置权限使除管理员以外其他人都无权查看。想一些防守措施能够让你减少很多痛苦。而且,对管理服务器来说,妄想症是优点。

未雨绸缪

并不是每一个问题都会找上你的门。利用空余时间去“招惹”问题并且确保系统还是能够完好运行。想想系统会出问题的各种可能性并且检查这些问题是否已经存在。

尽可能自动化去检查问题,但是要确保问题在你的掌控之中,你要注意本应该看到的报警是否如期到来。收到你最关键的服务器宕机警报不能让你得到任何奖励。

特别注意安全性

安全方面的投入是要与你所保护的数据成比例的。要知道你在保护什么,知道你所保护数据的“主人”。运用一些最佳的实践方案,比如最小特权、定期补丁、监控关键服务以及漏洞检测。仅仅只运行你所需要的服务。时刻警惕任何强行闯入或者系统受损的迹象。准备好上报渠道能够让你知道什么时候以及向谁汇报这些系统受损现象。

不要忽视日志文件

例行检查日志文件能够在正常的服务器和服务受威胁之前发出警告。检查错误和警告。使用一个监控日志的工具或者自己写脚本。没有人有时间去看你日志中所有的信息。

伯乐在线推荐阅读:《写给开发者:记录日志的10个建议》和《最佳日志实践

备份一切

制定一个好的备份策略即使服务器已经做了同步。一个同步服务器上的错误同样也是错误。测试好备份系统。确保在你要使用它们之前是没有问题的。

在你能够承担的情况下多雇佣一位员工。尽可能零容忍单点故障,即使是你自己。

像自己的时间一样考虑其他人的时间

系统管理员有点像一个自大的小男孩。我们在自己的神奇领域是奇才。但是尽管如此,我们还是要准时参加会议并且别人向你寻求帮助的时候及时回复别人,即使只是说一句我正在解决他们的问题。礼貌地对待顾客,即使他们找不到怎么运行命令行。他们可能在他们自己领域是个专家级魔术师,如果他们不是的,那就正好说明我们是如此的重要。

保持时刻通知用户

确保用户能够预料将会发生什么,特别是计划会有重大更新时。这些可以让他们对你抱有信心,并且信任他们所依赖的服务。交流、透明化、使用票务系统还有就是要留意多长时间能够解决问题。

避免太过自我,让别人喜欢你

系统管理员没必要让人难以接近和自大。实际我这几年与我合作过真正厉害的系统管理员从没显示出任何的优越感。他们没必要这样。

不要停止学习新技能

就像逆水行舟,不进则退。经常找些新东西来学习。你将准备好承担新的责任甚至可能在裁员中幸存。如果你没有把握去学什么,那就去看心仪工作的职位介绍。怎么让自己符合标准?那些技术是高需求的吗?你是否可以每天腾出一点点时间来学习新东西呢?

伯乐在线推荐阅读:《学习新技术的 10 个建议

寻求平衡生活

找一些自己喜欢的事来平衡生活甚至可以找点活动可以奖励你些东西,这些东西可以跟做一名天才般的并且极具洞察力的系统管理员完全无关。不要把自己的价值栓在一匹马上。即使是你非常热爱工作,也不要让它成为唯一使你感到愉快和重要的事。你甚至可以被扔到车底下,但是也不能让车胎印记印在你的生活中。工作不是全部。不要被办公室政治束缚。去看一场真实的比赛并且努力成为一个你所钦佩的那种人。

转载自 dsy851009

在工作中经常遇到当产品上线出了bug后,第一个受到指责的是测试人员,”测试为什么当初没有发现这个问题呢”,这种情况在现实工作中数不胜数,也许他们把测试人员当”超级魔法师”了,经过测试之手的东西就完美无瑕了,这就属于角色定位问题,当定位好自己的角色后,在协商角色内容时,就有了在可能出现的任何情况下现的问题时首先确立对自己预期的基础。

一、善于提出问题

测试人员在需求分析或者在测试过程中不问问题,不是不能测试,只是不能更好的测试,问问题是测试人员对项目发挥作用的基础,不问问题,测试就没有目标,思路不够开阔,分析不透彻,只是呆板的机械的测试固有功能,之前听阿里一位同事讲过,他们在发布的任何产品的测试报告中必须体现出项目的风险点是什么,如果不思考不分析,风险点是不容易提出的,那么测试意义就会打折。

二、与开发人员高度配合

为程序员提供支持,才是测试员使命的关键部分,当程序员还在编写代码或者编写完成待提测时,必要时测试人员能够提供测试工具为开发人员快速验证使用,而在程序交付后,应该马上启动测试(当然前期测试准备工作需要充分),尽可能建立最短、最快的反馈环路。力求当程序员还在苦苦思索上个bug如何解决时,测试已经开始寻找更多的程序问题,最理想的状态是程序员为了修改bug团团转,是程序员而不是测试人员成为项目的瓶颈,降低项目潜在风险。而且这里可以加一点测试人员的角色,就是对bug定位问题,不能只看问题现象,需要深入问题本质,一层一层扒开它的面目,为开发人员节省时间,缩短bug生命周期。

三、认清重点

测试员不会发现所有的问题,测试员的任务就是找出并报告重要的程序问题。那么假设一下,为了发现程序所有的错误,测试员必须检查所有可能有问题的地方,要在有可能发生的不同条件下观察这些地方,还需要一种十分可靠的方法,当所有类型的错误发生时,你都能够识别出来,那么如果一个测试人员能做到这些,要么是这个产品特别简单,要么测试员的想象能力有限。当我们知道并承认自己不能做所有的事之后,测试员必须选择如何利用自己的有效时间。

经验总结:迅速找出重要程序问题。

1、首先测试变更的部分,然后回归老功能,识别新变更带来的风险;

2、首先测试核心部分,即关键和常用功能;

3、首先测试功能,再测试可靠性,考虑各种异常场景;

4、具备判别bug风险等级的能力;

….等等

当然这里要求测试人员对产品有绝对的熟悉了解,更快捷的找到问题;

四、测试不能保证质量

测试人员不是质量卫士,测试既不会提高质量,也不会降低质量,质量好不好代码底子就在那里,质量源于构建产品的人,听起来很不可思议,但这也是他们要背负的沉重负担,测试员使命中另一部分就是帮助他们对付真正的负担。但如果测试员认为自己是项目团队中唯一关心交付好产品的人,就不能很好的完成这个使命,说明测试员没有认清自己的角色,测试员的测试和错误报告提供了促进质量保证的信息,而最终保证质量的是整个团队。所以测试员永远不要做看门人,否则是对整个产品的不负责任。当你扛起整个产品质量的全部责任时,团队的其他成员可以放松一点,甚至会大大放松,如果问题遗漏没被发现,其他成员想当然的会来指责你,为什么你没发现问题呢,并且同时伴随的还有对你工作量的质疑。

这里再举个例子,曾经待过一个敏捷团队,在那里从来没有上述问题,为什么呢?因为如果线上出问题,首先找到的是相关的开发人员,他要付最大的责任,那么你就奇怪难道测试员就一点干系没了?非也,测试员有测试团队自己的考核标准,会从自身找问题,自然也不会轻松罢了。而这种模式的利好在哪里呢?利好在于当开发人员在写代码时候,他就会考虑到质量问题,如果出bug即便测试员没发现,他们也脱不了干系,那么在接下来的测试工作中,开发人员起了很大的推动作用,这样就整个团队就达成了一个目标,整个去保证质量。

总结:质量是需要团队的所有角色参与者一起分担的。

为什么要重构?

  • 重构的目的是使软件代码更容易被理解和修改

  • 重构能够帮我们更有效地写出强健的代码

  • 重构可以帮助我们更快速地开发软件,阻止系统腐败变质,提高设计质量

  • 重构不会改变软件可观察的行为——重构之后软件功能一如既往

什么时候应该重构?

  • 添加功能时重构

  • 修补错误时重构

  • 复审代码时重构

什么时候不应该重构?

  • 代码根本无法工作或者太糟糕,重构还不如重写来的简单

  • 在项目的最后期限,应该避免重构

代码的坏味道

  • 重复代码:多个代码类里看到相同的代码或程序结构

  • 过长函数:函数是否有多种职责

  • 过大的类:单个类做太多的事情,其内往往就会出现太多实例变量

  • 过长参数列:过长的参数会难以理解,是否能用一个对象替代

  • 发散式变化、霰弹式修改:如果受外界变化的影响而需要对类的代码做出多出修改,那应该把变化的内容放在一起成为类或函数。

  • 依恋情结:判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些数据放在一起

  • 数据泥团:放在对象里的属性应该和对象的意义仅仅关联

  • 基本类型偏执:该用对象的时候用对象,改用基本类型变量的时候用基本类型变量

  • switch惊悚现身:只当使用switch语句能使代码结构更清晰的时候才用

  • 平行继承体系:当你为某一个类增加子类时,也必须为另一个类相应增加一个类,解决方法是使用引用替代继承

  • 冗赘类:如果一个类不值得存在,那就让它消失

  • 夸夸其谈的未来星:无用的抽象类、无用的抽象参数都可以移除

  • 令人迷惑的暂时字段:只用一次的临时变量一般情况下都可以移除

  • 过度耦合的消息链:清楚消息链上的对象的作用,能否把使用链上的该对象的代码提炼到独立函数中

  • 中间人:如果某个类接口有一半的函数都委托给其他类,说明可以移除委托类,让代码直接和真正负责的对象打交道

  • 狎昵关系:多个类是否具有共同点太多,可以提取共同点为抽象类

  • 异曲同工的类:是否存在不同名字的类或函数,做着相同的事

  • 过多的注释:注释是在别人无法单从你的代码就从快速理解其作用时使用

重构列表

1.重新组织函数

  • 提炼函数:保证提炼代码可以强化逻辑清晰度,并能通过函数名称解释函数的用途

  • 内联函数:如果函数比较短,且其内容就和其名称一样清晰易懂,就应该在函数调用点替换为函数本体,然后移除该函数。

  • 内联临时变量:你有一个临时变量,只被一个简单表达式赋值一次并只被引用一次,那就把临时变量删掉,并在引用点替换为对表达式自身。但如果是被引用多次且不止一个函数内使用到,那么可以将这个表达式提炼到一个独立函数中,并将这个临时变量的所有引用点替换为对新函数的调用。

  • 引入解释性变量:你有一个复杂的表达式。应该拆分复杂表达式为多个部分,分到多个临时变量,并以此变量名称来解释这个被拆分的表达式的用途。

  • 分解临时变量:如果程序有某个临时变量被多个用途的代码赋值过,并且会对代码的清晰度有影响,那么可以针对每次赋值,创造一个独立、对应的临时变量。

  • 移除对参数的赋值:代码对一个参数进行赋值,如果会应该代码的清晰度,则以一个临时变量取代参数的位置。

  • 替换算法:你想要把某个算法替换为另一个更清晰的算法。

2.在对象之间搬移特性

  • 搬移函数:你的程序中,有个函数与其所驻之外的另一个类进行更多交流:调用后者,或被后者调用。在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除。

  • 搬移字段:你的程序中,某个字段被其所驻类之外的另一个类更多地用到。在目标类新建一个字段,删除源字段,并修改引用源字段的地方为新字段。

  • 提炼类:某个类做了应该有两个类做的事。建立一个新类,将相关的字段和函数从旧类搬移到新类。

  • 将类内联化:某个类没有做太多事情。将这个类的所有特性根据其职责搬移到另一个或多个类中,然后移除原类。

  • 隐藏“委托关系”:如果调用的几段代码之间是有逻辑上的关联,那么可以考虑把其中一个调用当做委托来调用另一段代码,用以隐藏调用的实际逻辑关系。

  • 移除中间人:某个类做了过多的简单委托动作。让客户直接调用受托类,可以的话删除委托类。

  • 引入外加函数:你需要为提供服务的类增加一个函数,但你无法修改这个类且这个函数其实只需要用到服务实例的属性,则可以在客户类中建立这个函数,并以参数形式传入服务类实例。

  • 引入本地扩展:你需要为服务类提供一些额外函数,但你无法修改这个类。建立一个新类并继承源类,使它包含这些额外函数。

3.重新组织数据

  • 自封装字段:你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。为这个字段建立getter/setter函数,并且只以这些函数来访问字段。

  • 以对象取代数据值:你有一个数据项,需要与其他数据和行为一起使用才有意义,将数据项归类变成对象。

  • 以对象取代数据:你有一个数组,其中的元素各自代表不同的东西([“15”,”hello”])。以对象替换数组,对于数组中的每个元素,以一个字段来表示。

  • 将单向关联改为双向关联:两个类都需要使用对方特性,则在关联函数中使其能够同时更新两条链接。

  • 将双向关联改为单向关联:两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。在关联函数中去除不必要的更新。

  • 以字面常量取代魔法数:你有一些字面数值,带有特别含义。创造一些常量或枚举的方式,根据其意义命名,并将上述的字面数值替换为这个常量。

  • 封装字段:你的类中存在一个public字段。将它声明为private,并提供相应的访问函数。

4.简化条件表达式

  • 分解条件表达式:如果你的条件(if-then-else)语句的代码很复杂,则可以从if、then、else三分段落中分别提炼出独立函数。

  • 合并重复的条件片段:在条件表达式的每个分支上有着相同的一段代码,则将这段重复的代码搬移到条件表达式之外。

  • 移除控制标记:在循环的表达式中,如果考虑到表达式的可读性,可以用break语句或return语句取代控制循环的条件表达式。

  • 以卫语句取代嵌套条件表达式:函数中嵌套的条件逻辑往往使代码不清晰,如果某些条件是极其罕见的,则可以允许把嵌套的条件提取出来进行单独检查。

  • 以多态取代条件表达式:你手上有个条件表达式,它根据对象类型的不同选择不同的行为。将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

  • 提炼继承体系:你有某个类做了太多工作,其中一部分工作是以大量条件表达式完成的。建立继承体系,以一个子类表示一种特殊情况。

5.简化函数调用

  • 函数改名:函数的名称未能揭示函数的用途,则修改函数的名称。

  • 添加参数:某个函数需要传入的参数很多时,可以替换为一个对象来作为入参,让该对象带进函数所需信息。

  • 保持对象完整:你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。

  • 引入参数对象:某些参数总是很自然地同时出现。以一个对象取代这些参数。

  • 移除参数:函数本体不再需要某个参数。将该参数去除。

  • 将查询函数和修改函数分离:某个函数既返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。

  • 令函数携带参数:若干函数做了类似的工作,但在函数本体中却包含了不同的值。提取相同的代码为统一调用的函数,以入参变量替换那些不同的值。

  • 以明确函数取代参数:如果一个函数根据入参值而采取不同行为且某些行为的代码量很大时,应该针对某些代码量很大的参数来建立一个独立函数。

  • 以工厂函数取代构造函数:你希望在创建对象时不仅仅是做简单的构建动作,将构建函数替换为工厂函数。

  • 封装向下转型:如果希望某个函数返回的对象可以直接操作,将向下转型动作移到函数中。

  • 以异常取代错误码:如果不是调用外系统的函数,可以改用异常或自定义异常直接抛出以表示某种错误情况,否则返回一个特定的代码或结果对象。

6.处理概括关系

  • 字段上移:两个子类拥有相同的字段,将该字段移至超类。

  • 函数上移:有些函数,在各个子类中产生完全相同的结果,将该函数移至超类。

  • 函数下移:超类中的某个函数只与部分(而非全部)子类有关。将这个函数移到相关的那些子类去。

  • 字段下移:超类中的某个字段只被部分(而非全部)子类用到。将这个字段移到需要它的那些子类去。

  • 提炼子类:类中的某些特性只被某些(而非全部)实例用到。新建一个子类,将上面所说的那一部分特性移到子类中。

  • 提炼超类:两个类有相似特性。为这两个类建立一个超类,将相同特性移至超类。

  • 提炼接口:如果某个类在不同环境下扮演截然不同的角色,使用接口;多个种类的服务对象,使用接口作为服务提供方来提供操作

  • 塑造模板函数:你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上所有不同,则可以替换为使用设计模式中的模板方法

7.大型重构

  1. 梳理并分解继承体系:某个继承体系同时承担两项责任。建立两个继承体系,并通过委托关系让其中一个可以调用另一个。

  2. 将过程化设计转化为对象设计:你手上有一些传统过程化风格的代码。将数据记录变成对象,将大块的行为分成小块,并将行为移入相关对象之中。

实践

  1. 不要过早发布接口,请修改你的代码所有权政策,使重构更顺畅。

  2. 对于一个重要的遗留系统,可以重构为封装良好的小型组件,然后你就可以逐一对组件做出“重构或重建”的决定。

  3. 重构能让你对问题的理解加深,让日后修改成本不再高昂

  4. 当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余

  5. 在性能优化阶段,首先应该用一个度量工具来监控程序的运行,让它告诉你程序中哪些地方大量消耗时间和空间,这样就可以找出性能热点所在的一小段代码,然后集中关注这些热点,短期看来,重构的确可能使软件变慢,但它使优化阶段的软件性能调整更容易,最终还是会得到好的结果。

  6. 多运用单元测试,重构的基本技巧-小步前进、频繁测试