<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_张逸:晴窗笔记</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/819/rss</id><updated>2012-02-07T04:40:04Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/819/rss"/><entry><id>http://www.cnblogs.com/wayfarer/archive/2012/02/07/2340781.html</id><title type="text">真实案例引起的对系统健壮性的思考</title><summary type="text">大年初四(2012年1月26日)上午，我在重庆移动某营业厅的自助客户端使用招商银行信用卡为我妻子充话费（我妻子的手机已经停机）。在插入信用卡并输入密码后，系统提示正在交易。大约几秒后，我的手机收到招行的短信，提示消费100元，但自助客户端仍然显示正在交易。此时的我已经有了不详的预感。果然，在等待大约一分钟，系统提示操作失败，之后系统崩溃，弹出了一个Windows命令窗口。因为我妻子的手机停机了，所...</summary><published>2012-02-06T16:25:00Z</published><updated>2012-02-06T16:25:00Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wayfarer/archive/2012/02/07/2340781.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/archive/2012/02/07/2340781.html"/><content type="html">&lt;p&gt;大年初四(2012年1月26日)上午，我在重庆移动某营业厅的自助客户端使用招商银行信用卡为我妻子充话费（我妻子的手机已经停机）。在插入信用卡并输入密码后，系统提示正在交易。大约几秒后，我的手机收到招行的短信，提示消费100元，但自助客户端仍然显示正在交易。此时的我已经有了不详的预感。果然，在等待大约一分钟，系统提示操作失败，之后系统崩溃，弹出了一个Windows命令窗口。因为我妻子的手机停机了，所以立刻可以确认上一次充值确实是失败的。而我的手机能收到信用卡的消费信息，则可以确认银行确实已经支付了100元（我之后查询了我的信用卡账单，确实存在这一笔消费记录）。&lt;/p&gt;  &lt;p&gt;之后，我又用现金充值话费，此次操作成功了，但却显示赠送的3元话费失败，提示相同Id的赠送记录冲突。这是因为系统规定了约束，要求当月同一个号码只能享受一次优惠。但问题是，我之前用信用卡的充值并未成功，也未收到赠送的话费。&lt;/p&gt;  &lt;p&gt;我没有机会能够看到该自助充值系统的设计与代码，但以我的开发经验，可以直观感受到这是事务出现了问题。这个简单的充值操作，实际上完成了四个职责：    &lt;br /&gt;1）调用第三方的银行支付服务，付费；     &lt;br /&gt;2）获取优惠策略，并计算优惠金额；     &lt;br /&gt;3）保存优惠记录，便于满足优惠的约束条件；     &lt;br /&gt;4）充值（包括付费金额+优惠金额）；&lt;/p&gt;  &lt;p&gt;显然，这四个操作必须放在一个事务范围内，并遵循ACID原则中的一致性原则。由于在该操作中，至少对第三方银行支付服务的调用是跨系统跨资源的，因此，事务必须是分布式事务。目前看到的系统问题，显然是在充值时，系统出现了故障，却未能将前面的两个操作回滚，导致执行结果不一致。结果，我悲催了：银行扣了款，优惠没落着，费用没充上。&lt;/p&gt;  &lt;p&gt;从直观表现看，我甚至有理由怀疑，对于充值的整个操作，系统是否使用了事务？？因为，倘若将这四个不同的操作作为一个服务放在事务中时，应该不会在系统提示正在交易时，银行就扣款成功。不过，考虑到对于这样的业务需求，使用事务基本上已经成为了常识，这个系统的设计者或者实现者应该不会犯如此低级的错误，那我只能善良地认为，该系统没有能够很好或正确地使用分布式事务。&lt;/p&gt;  &lt;p&gt;我不知道，该系统是基于什么平台开发，是使用了.NET的DTC，还是Java的JTA。然而基于分布式事务的基本原理来看，这是一种将对多个资源和服务的访问放在同一个事务中的情况。此外，系统调用的第三方银行支付服务必然也是使用了事务的，它会作为整个分布式事务的事务提交树中的子节点，而支付服务的事务则为根事务，是整个事务的总体协调者。由于访问的资源并不相同，即使各个操作放在了自己的事务中，也无法保证满足ACID，因此，这里应该使用两端式提交(two-phase commit)。&lt;/p&gt;  &lt;p&gt;重庆移动在提出需求时，必然首先考虑自身的利益，因而系统充值服务包含的四个操作，其执行顺序必然是：首先调用第三方银行支付服务，如果成功，再获取优惠策略，并获得优惠金额；然后充值（从故障表现看，似乎记录优惠信息的操作却在充值操作之前）。考虑简单的情况，假设后三个操作访问的是同一个资源（主要应该是数据库）。那么支付服务事务作为根节点，应该协调银行付费服务事务和充值事务的投票结果，然后再决定是否提交。当所有的参与者表示Prepare，才会提交。而在提交过程中如果出现问题，就必须回滚事务。从故障表现来看，似乎该事务并未采用两段式提交，因为它没有协调投票结果的过程（因为我的手机首先收到了银行的消费信息，优惠记录也保存了，否则不会出现优惠记录ID冲突）。此外，故障出现时，系统一直显示“正在处理……”字样，并在约1分钟左右提示故障。这说明系统可能考虑了Timeout值的设置。如果采用了两段式提交，在各个参与者准备就绪后，如果出现了问题，就应该存在未决（In-Doubt）事务，在规定时间内没有解决，分布式事务会中止整个事务，并回滚。&lt;/p&gt;  &lt;p&gt;所以，我在这里有理由相信该系统即使使用了事务，也没有很好地用好事务，尤其是分布式事务。在这里，第三方银行服务是没有任何问题的，它自身的事务必然是完整的，但此时它作为整个事务的参与者，是事务提交树的子节点，却没有被很好地协调。&lt;/p&gt;  &lt;p&gt;当故障出现后，系统在提示“操作失败”后的表现是崩溃，而不是回到主界面，这也说明了系统连基本的异常处理也可能没有做好。&lt;/p&gt;  &lt;p&gt;这里，事实上还存在一个小插曲。那就是在我询问了营业厅的营业员后，该营业员打开机器，查询了日志文件夹下的交易日志，并没有查询到我的充值记录。后来，她才醒悟，说道自助客户端并不会记录银行卡充值的交易信息。这让我倍感纳闷。虽然现金充值和银行卡充值是两种不同的充值方式，但从抽象层面来看，它们的行为是完全是一致的，充值方式不过是充值策略的两种体现罢了。从设计的角度来看，这是一个典型的Template Method模式。因为对于Charge操作来看，除了支付的实现不同之外，其余操作包括充值、获得优惠策略并计算优惠金额、事务处理、异常处理、资源管理以及日志记录，都应该是完全相同的，为何不能在系统中统一处理呢？显然，它应该是定义在应用服务层的一个统一服务接口（它可以是一个抽象类，也可以作为接口，并另外定义一个抽象类实现它，并实现共同的逻辑），并提供Cash和Card的两个实现类。系统会根据输入实例化不同的实现类。&lt;/p&gt;  &lt;p&gt;而对于系统维护而言，日志本身就是必不可少的信息（也许系统内部有日志可供维护人员查询，但日志可以是分级的，以便于不同角色根据需要对日志进行查询）。对于这种涉及到金钱交易的业务，日志记录更显得重要，因为它能够减少很多消费纠纷。这样的设计真的让我很不解。不错，我没有看到系统的实现（我很有兴趣能够看看这个系统的设计与实现，可惜没有这个机会），但根据这些故障表现，确实可以分析得到，这样的系统没有很好地保障系统的健壮性。这个真实案例，很可以值得我们软件从业人员深思。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wayfarer/aggbug/2340781.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wayfarer/archive/2012/02/07/2340781.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wayfarer/archive/2011/11/15/2249556.html</id><title type="text">调整Virtual Box硬盘大小</title><summary type="text">我在Mac下使用Virtual Box安装Win7的虚拟机。因为之前装过Win7的32位版。现在因为机器内存升到8G，就可以划出4G来支持Win7虚拟机。所以就重新安装了Win7的64位版。在创建虚拟机的硬盘时，我选择了Virtual Box的默认容量20G。我看到Virtual Box告知的是这个硬盘容量是可以动态调整的，谁知道这其实是一个误导。这里所谓的动态调整并不能超过设置的值，即我设置的2...</summary><published>2011-11-15T04:12:00Z</published><updated>2011-11-15T04:12:00Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wayfarer/archive/2011/11/15/2249556.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/archive/2011/11/15/2249556.html"/><content type="html">&lt;p&gt;我在Mac下使用Virtual Box安装Win7的虚拟机。因为之前装过Win7的32位版。现在因为机器内存升到8G，就可以划出4G来支持Win7虚拟机。所以就重新安装了Win7的64位版。在创建虚拟机的硬盘时，我选择了Virtual Box的默认容量20G。我看到Virtual Box告知的是这个硬盘容量是可以动态调整的，谁知道这其实是一个误导。这里所谓的动态调整并不能超过设置的值，即我设置的20G是存储分配的上限。所谓动态分配其实是一种节约磁盘空间的做法。意思是当我们在创建虚拟机的硬盘时，可以设置足够大。只要这个硬盘没有用完，这些空间是可以被主机所共享的。而Win7的64位版所占用的硬盘空间又远远超过了32位版。在安装了操作系统后，剩下的空间就不多了。在安装了SQL Server 2008后，再想完整安装Visual Studio 2010，空间就不够了。我之前认为硬盘容量可以动态调整，所以在Visual Studio提示空间容量不够时，我仍然选择“继续”，结果安装错误。&lt;/p&gt;  &lt;p&gt;那么，该怎么解决这一问题呢？重新安装吗？那就实在太悲催了。虽然现在并没有安装什么软件，但安装操作系统和数据库就挺耗时的。于是，我试着调整Virtual Box的硬盘大小。可是在Virtual Box的管理工具中，并没有找到修改硬盘大小的选项。通过Google，我找到了通过命令行修改磁盘空间的办法，前提是Virtual Box的版本必须是4.0及以上。&lt;/p&gt;  &lt;p&gt;这个命令：&lt;/p&gt;  &lt;p&gt;VBoxManage modifyhd uuid –resize 40960&lt;/p&gt;  &lt;p&gt;这里的40960就是你要调整的容量，即40G。命令中的uuid也可以用vid文件名代替。不过我的文件名不幸包含了空格。或许通过双引号或&amp;quot;/“可以支持空格的文件名，但我没有尝试，因为使用uuid是更好的做法。至于命令行的输入，在Mac下，直接在Terminal下输入即可。因为安装了Virtual Box时，会将VBoxManage添加到路径中，Terminal是可以识别该命令的。&lt;/p&gt;  &lt;p&gt;要使用uuid，则需要获得当前虚拟机的uuid。在管理工具中无法获得，因此，我们应通过命令行获得：&lt;/p&gt;  &lt;p&gt;VBoxManage list hdds&lt;/p&gt;  &lt;p&gt;该命令会显示所有的虚拟硬盘。你可以通过Location来判断你要调整的硬盘。获得uuid，就可以通过modifyhd调整硬盘大小了。注意，在调整硬盘之前，一定要先关闭该虚拟机。&lt;/p&gt;  &lt;p&gt;补记：当我通过modifyhd命令调整了磁盘空间后，通过管理工具查看vid文件，容量确实发生了变化，达到了预期的结果。于是我认为可以高枕无忧了。哪知道在Virtual Box下打开该虚拟机后，看到C盘的空间仍然是20G。这让我不禁郁闷不已。于是我想，可能在Virtual Box中还有什么设置。可是看了所有Virtual Box的管理菜单，都没有能够找到。我甚至在Storage中，移除对vid文件的引用，并重新加载，仍然没有变化。我又尝试着重新启动Virtual Box，甚至重新启动计算机，还是如此。究竟问题出在哪儿呢？正在百思不得其解，甚至打算删除该虚拟机，重新安装。可是想到还要去安装操作系统，以及相关软件，就不寒而栗。于是打算退而求其次，在Virtual Box中为其增加一块磁盘。结果，增加之后，启动虚拟机，发现并没有在计算机中增加新的磁盘。突然灵机一动，想到是否是计算机管理中的磁盘分区管理的问题呢。于是，打开Win7&amp;quot;计算机管理”中的“磁盘管理”，发现新增加的磁盘显示为未分配，而原来的磁盘容量赫然为40G，其中却有20G未曾分配。于是选择扩展磁盘，失踪的20G磁盘果然被找了回来。Virtual Box的硬盘大小调整终于取得成功。&lt;/p&gt;  &lt;p&gt;因此，通过Virtual Box命令对磁盘进行调整后，一定要记得在Win7操作系统中扩展磁盘。这就好似你为你的计算机新添加了一块磁盘一样，需要进行同样的操作。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wayfarer/aggbug/2249556.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wayfarer/archive/2011/11/15/2249556.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wayfarer/archive/2011/11/09/2241887.html</id><title type="text">LA工作第二周体会</title><summary type="text">第一周的新鲜感过去后，第二周我就能够以平常心来面对周围的新环境了。除了语言和饮食不同，终究还是软件开发的工作，一定是要遵循软件开发的规律的。在这短短的两周时间内，我谈不上有什么收获，能够让我的能力再上一个台阶。但一些体会还是有的，虽然有些散乱，仿佛思想的片段，如野马，如浮云，若能及时捕捉并加以记录，这些体会就能够潜移默化地改变自己，或许也能改变阅读这篇博客的你。 这一周，我打算谈一谈我对能力的体...</summary><published>2011-11-08T16:43:00Z</published><updated>2011-11-08T16:43:00Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wayfarer/archive/2011/11/09/2241887.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/archive/2011/11/09/2241887.html"/><content type="html">&lt;p&gt;第一周的新鲜感过去后，第二周我就能够以平常心来面对周围的新环境了。除了语言和饮食不同，终究还是软件开发的工作，一定是要遵循软件开发的规律的。在这短短的两周时间内，我谈不上有什么收获，能够让我的能力再上一个台阶。但一些体会还是有的，虽然有些散乱，仿佛思想的片段，如野马，如浮云，若能及时捕捉并加以记录，这些体会就能够潜移默化地改变自己，或许也能改变阅读这篇博客的你。&lt;/p&gt;  &lt;p&gt;这一周，我打算谈一谈我对能力的体会。作为一名Dev，能力是自己的立身之本，也是养家糊口的必须技能。我常常觉得，作为一名还算不错的技术人员，要挣大钱还是不容易的，但心里至少不会有失业的恐慌，总觉得自己身有一技之长，到哪里不能混口饭吃呢。所以只要不是被刺激，心态总还是比较淡定的。然而，这其中暗藏的风险就是，我的生活、我的家庭乃至我的未来就完全绑在这一根绳子上了。如果能力跟不上时代的发展，我们就会成为死在沙滩上的前浪，连一个印子都留不下来。这么一想，能力的打磨就至关重要了。那么，要成为一名优秀的Dev，需要具备哪些技能呢？在这一周通过和美国的同事Pair，我的一些观点得到了实践的印证。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1、专和博的能力&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;毫无疑问，专是必须的。没有专，只有博，就好比建在沙滩上的城堡，经不得风吹雨打，很容易坍塌。若要具备专的能力，就需根据自己的性格、兴趣以及工作特征来选择需要攻坚的方向，例如算法、网络、数据库、业务分析、架构设计等。这个无需多言。那么博呢？或许对于某些软件开发工作而言，“博”并非必备，但就一般的项目开发来说，确实需要具备更为广博的知识。这年头，只知道编码的程序员很难获得更好的机会。编码能力是基本，但如果不了解许多与编码有关的知识，例如设计、测试、数据库、构建脚本、工具，就很难参与到项目开发中。这些能力并不需要程序员一定要掌握、精通，但必须有所了解，并根据项目情况，决定是否需要深入学习。例如，在前一个项目中，我第一次接触到Cucumber，学会如何编写Regression Test的Feature。到了这个项目中，我要编写Twist的Test就要容易许多了。至少我不会茫然。当然，对于Regression Test而言，我还有很多困惑与体会要说，希望能有时间就这个问题专门写一篇博客。又例如对于编程语言。也许作为主流语言而言，了解Java或C#其中一门就已经很不错了。如果你一直从事.NET开发，似乎也没有学习Java的必要。可是为什么不去尝试一下呢。即使无需使用Java开发项目，了解Java的实现机制，对于开拓自己的眼界总是有好处的。何况对于项目开发而言，不定哪一天就需要你开发Java项目了。同理，我们不能只局限于一类语言。例如了解动态语言，函数语言等，了解它们的新奇之处，一定会对自己的编码能力有很大的帮助。现代的软件项目开发，越来越向着多语言开发的方向前进了，掌握多个语言，多个开发平台已经成为了程序员的必备要求。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2、学习能力&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;正是因为需要博，学习能力才显得如此的重要。坦白说来，现在有多少开发人员运用的知识都是自己的教师传授的呢？具备超强自学能力的程序员，即使起点很低，将来一定会走在其他程序员的前面。这是一场马拉松赛跑，比的是耐力、韧劲，当然还要有好的方法。在LA的第二周，我和Thoughtworks美国的一位老员工Sheroy一起Pair。我们需要对项目进行性能测试。我们选择了JMeter作为性能测试工具。这个工具我们事先都没有用过。不过，另外一个团队的成员曾经使用JMeter写过一个Test Plan。我们就参照着这个Test Plan以及JMeter提供的文档，开始学着使用JMeter来编写性能测试的计划。经过一天的实践，我们很好地了解JMeter的基础知识，并能够比较好的完成任务。这样的例子在我们公司俯首即是。人人皆善于学习，乐于学习。我们有很好的学习氛围，我们每天有Lunch Session，我们愿意在项目开发中尝试一些新工具或者新框架，我们愿意为新的技术去做Spike。正是因为这种学习的劲头，我们可以快速地进入项目，快速地掌握新的知识。坦言之，我在进入公司之后，曾经以为自己还算不错的学习能力，在公司同事面前就显得相形见拙了。我觉得自己的学习能力不够用了。这个压力是个好事，因为它可以促进我不断前进。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;3、解决问题的能力&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;事实上，这个能力与学习能力一脉相承。然而，它们又不完全相同。若要具备相当强的解决问题的能力，必然具备好的学习能力。因为很多问题是我们不曾遇见过的，也可能是我们在使用新工具、新语言、新框架中面临的问题。如果没有好的学习能力，就很难找到解决问题的钥匙。然而，仅有学习能力是不够的，解决问题需要方法。例如通过调试，通过查看日志，或者有效地搜索Google。解决问题还需要经验，具有丰富经验的开发人员即使面临新问题，也能够根据过去的经验找到快速解决问题的途径。仍然是这一周的开发工作。我和Sheroy在用JMeter写性能测试时，发现同时启动多个线程模拟并发用户登录时，有的登录行为出现了错误。我们对于登录的设置是正确的。如果单独执行JMeter的测试计划，也没有任何问题；但在Jenkins上执行时，就会出现这样的错误。我没有想到任何解决办法。但Sheroy通过分析执行日志，敏锐地发现登录行为的错误总是发生在上一个持续集成任务执行完毕之后。这个任务是用于完成性能测试环境的部署。这个部署任务与性能测试任务是串行执行的，性能测试在部署之后执行。Sheroy认为，可能是部署刚刚完成，性能测试任务就立刻执行，导致登录的请求未能得到服务端的正确响应。他尝试在JMeter测试计划中增加了一个等待时间，问题就迎刃而解了。&lt;/p&gt;  &lt;p&gt;整体来看，解决问题需要耐心、细致，善于从普遍性中找到特殊性，可能这个特殊性隐藏得非常深，那么就需要去比较正常和异常情况下，它们的环境、前置条件究竟有何不同？在什么情况下，这个问题会反复出现？只要找到了这种特殊性，往往就能发现端倪，进而想到解决问题的方案。就这一点而言，我还存在许多不足。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4、部署环境的能力&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;毫无疑问，这是我的短板。我最讨厌的就是配置、安装与部署。我甚至没有耐心去看安装指南、配置向导。尤其是对于一些开源框架或工具，在文档极度缺乏的情况下，我总是难以快速地完成框架或工具的搭建。相对而言，我更喜欢一键式安装的傻瓜做法。可是在现在的软件项目开发中，我们常常不限于只使用一个工具，而且使用的工具也不只限于图形化界面。也许有人会说，开发人员只需要关注自己的编码能力就可以了。可是我们开发出来的软件总是需要部署才能正常工作的，如果对软件的部署不了解，怎么交付给客户使用呢。也许有人又会说，这可以交给专门的部署人员来做啊！那么，作为实现功能的你而言，你怎么给你的部署人员说明整个部署流程？而且，若要顺利地进行软件开发，没有一个完整的开发环境，开发人员又该怎样开展工作呢？因此，开发人员应该具备一定的部署环境的能力。尤其随着DevOps变得越来越重要，这项能力也会扮演着越来越关键的角色。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;5、交流能力&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;不可避免，还是要提到交流能力。可以说，对于这一能力，无论如何强调都不过分。我在LA工作的这两周，因为语言问题已经强烈地感受到了这一点。就在本周四，我需要完成Story 271，但我对Story的业务逻辑了解得不够，所以就找到我们的BA Jacky。他是一位台湾人，不过几乎已经不怎么会说国语了。由于我对这一部分业务缺乏足够的了解，所以我们之间的交流从我事先想象的10分钟，延长到了半个小时，并在Jacky连说带画的努力阐释下，我才弄明白了这个Story要求我们做的到底是什么。这就是交流的必要性。我曾经接触过一些刚刚踏入这个行业的程序员，他们都愿意努力的学习，但在交流能力上普遍欠缺。一个问题难以表达清楚，也很难获得正确的理解。&lt;/p&gt;  &lt;p&gt;整体而言，无论是哪方面的能力，提升都是无止境的。能力是我们的立身之本，提升能力就能提升我们的价值，同时，也能够改善我们的生活，进而改善我们这个行业。这就是我的一点体会。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wayfarer/aggbug/2241887.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wayfarer/archive/2011/11/09/2241887.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wayfarer/archive/2011/11/07/2239148.html</id><title type="text">LA工作第一周体会</title><summary type="text">与一群国外的Dev一起工作是我从未有过的经历，在陌生的国度，陌生的团队，陌生的客户，做着陌生的项目，对我而言，Everything is new。我们在客户这里，仍然采用典型的敏捷方式：故事墙、站会、用户故事、结对编程、持续集成、TDD甚至BDD……几乎所有的敏捷实践我们都会运用。在加入这个项目时，已经进行到第7个迭代，整个项目的框架已经比较成熟。我们的任务是尽快熟悉业务和整个技术框架，并为新的项...</summary><published>2011-11-07T05:39:00Z</published><updated>2011-11-07T05:39:00Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wayfarer/archive/2011/11/07/2239148.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/archive/2011/11/07/2239148.html"/><content type="html">&lt;p&gt;与一群国外的Dev一起工作是我从未有过的经历，在陌生的国度，陌生的团队，陌生的客户，做着陌生的项目，对我而言，Everything is new。我们在客户这里，仍然采用典型的敏捷方式：故事墙、站会、用户故事、结对编程、持续集成、TDD甚至BDD……几乎所有的敏捷实践我们都会运用。在加入这个项目时，已经进行到第7个迭代，整个项目的框架已经比较成熟。我们的任务是尽快熟悉业务和整个技术框架，并为新的项目做好充分准备。在接近3周的时间内，我们都会和客户以及美国Thoughtworks的同事们进行Pair，通过实际的开发来熟悉和了解业务与技术。然后，在剩下的两周内为新项目做Inception。&lt;/p&gt;  &lt;p&gt;项目是基于.NET Framework 4.0进行开发的，采用的技术包括C#, VB.NET, Ext Js, SQL Server 2008。IoC容器为Structure Map，NUnit作为单元测试框架，Moq作为Mock测试框架。我们使用了Jenkins(即Hudson)作为持续集成工具，使用了Thoughtworks的产品Twist作为回归测试和集成测试工具，并使用了Powershell作为构建脚本，Git作为源代码控制工具。&lt;/p&gt;  &lt;p&gt;来到LA的第一周。除了第一天参加了几次Meeting，了解了整个项目的情况尤其是业务逻辑之外，第二天就迅速进入团队，开始结对编程。整个第一周，我完成了2个Bug Fix，同时，对Regression Test出现的问题进行了修复，并参与了一个Story的开发。在这一周，我并没有Switch Pair，一直是和客户这边的一位Dev（名叫Andrew）进行合作。&lt;/p&gt;  &lt;p&gt;回顾这一周的工作，我的感受如下：&lt;/p&gt;  &lt;p&gt;1）业务逻辑的了解比技术更重要&lt;/p&gt;  &lt;p&gt;作为一名Dev，可能首先会想到项目会使用什么技术，我对这些技术了解吗？总之，会首先关注一切与技术有关的东西。在进入项目之前，我特别关注了这些内容，并抓紧一切时间为这些技术做储备。当然，我们也希望了解业务逻辑，但由于前期准备时缺乏这方面的条件，我们所能了解的就是项目与Healthcare有关，项目内容有些接近CMS。然而，到了项目之后，我们才发现，技术并非决定你能否快速进入团队，并开始开发和实现的关键。如果不了解业务逻辑，不明确领域术语，我们将很难进行沟通和交流。尤其对于现在这个项目，由于项目已经做了一部分。对领域的了解就更加重要了。对于一名有着多年经验的Dev来讲，其实技术并不会成为制约你进行项目开发的主要瓶颈。在这个项目中，有很多技术都不是我掌握的，但我们仍然可以快速进入开发活动。这是因为Pair Programming可以很好地完成知识共享和传递，我的Pair可以像Mentor一样来带领我迅速进入状态。&lt;/p&gt;  &lt;p&gt;2）交流是项目开发的根本&lt;/p&gt;  &lt;p&gt;在国内做项目，交流或许也会成为障碍，但因为语言相同的缘故，我们往往将交流活动忽略了，似乎觉得这是顺乎自然的事情。在这一周的工作中，我仿佛进入了另一个世界，耳边充斥地都是自己难以理解的语言。虽然我有一定的英语基础，但真正和这些母语为英语的Dev合作时，才发现我所掌握的英语单词和语法都变得不够用了。我的舌头似乎也打结了，我很难理解Pair所要表达的意思，自己也很难向Pair表达我的意见。这就导致开发效率受到很大影响。即使任务已经完成，整个实现对我而言，仍然似是而非，还需要自己下来看Story的描述，看源代码。例如，在我们项目中已经实现了比较好的Validation机制，但为了实现一个相对较小的Story，由于交流的问题，我们的实现被严重阻碍了。&lt;/p&gt;  &lt;p&gt;此外，在我们项目中完全具备现场客户的条件，因而交流更是成为重中之重。我们的BA团队既有TW的，也有客户的。他们都写了很不错的User Story。在我们实现这些User Story时，如有不明白之处，都需要尽快咨询BA，通过交流消除歧义。而在实现之后，必须和BA做Show Case，以尽快获得反馈。这一点非常重要，也是敏捷的核心价值观体现。&lt;/p&gt;  &lt;p&gt;3）好的习惯很重要&lt;/p&gt;  &lt;p&gt;第一周，和我Pair的Andrew是来自客户的一位实习生。他刚从大学毕业，进入项目大约三个月时间。在大学期间，他只学习了C++，对.NET、Javascript以及CI等内容都不熟悉。换言之，他现在所掌握的所有技术，都是在项目中学到的。虽然是这样一个Intern，但我发现他已经具备了非常良好的编码素质。在开始一个Story时，他会首先在Twist编写Regrssion Test Sceinario。而在实现代码时，也会尝试着通过Unit Test来驱动实现。在提交代码时，会合理地运用Git命令。例如在开发前，会通过Git Status检查当前状态，看是否有变更。在Commit时，如果发现提交的内容有冲突，他会非常慎重地处理Merge。编写代码时，会严格遵循我们制定的编码规范。虽然，他在开发方面的经验还有很多欠缺，但无疑已经有了一个好的开始。我想，通过这个项目的锻炼，在Thoughtworkers的言传身教下，只要他愿意继续努力，应该会有一个很好的发展前景。&lt;/p&gt;  &lt;p&gt;同时，对于团队而言，这样良好的编码习惯，就像所谓的“童子军军规”一般影响这团队的每个人，我们欠下的Technical Debt就会少很多，这对于后期的维护、修改以及将来后续的开发，都有非常大的帮助。&lt;/p&gt;  &lt;p&gt;4）学会寻求帮助&lt;/p&gt;  &lt;p&gt;每个人所掌握的知识总是有限的。或许你的能力足够让你解决任何问题，但考虑成本问题，若能适当地寻求帮助，你既能够快速完成任务，同样能够学到你希望学到的知识。即使寻求帮助后，没有得到你所期望的结果，至少说明，我们可以拒绝某些选项，这同样可以节约时间成本。最关键的一点需要时刻记住，我们是一个Team。&lt;/p&gt;  &lt;p&gt;在第一周的Story开发中，由于我和Andrew都对Validation的机制不太熟悉。我们尝试了多种方法，希望解决问题，但都不奏效，这样反复的尝试已经耽搁了大约一天的时间。后来，我们主动找到另一位Thoughtworker-Eric，他已经在项目呆了较长时间。了解我们的问题后，Eric仅用了不到10分钟的时间就解决了这个问题。事实上，这个问题的解决方案需要一个小技巧，在Label中设定我们事先指定的CSSClass，就可以显示那些Validation Message。&lt;/p&gt;  &lt;p&gt;5）知识分享&lt;/p&gt;  &lt;p&gt;毫无疑问，只有充分的知识分享，才能有效发挥团队的作用。尤其对于新加入团队的成员，如果没有足够好的知识分享，或者团队的老成员不具备这方面的意识，将自己拥有的知识传递给新成员，新成员就很难融入团队，也很难快速地贡献自己的Effort。在知识分享的过程中，通过Meeting和Session，或者阅读文档的方式当然是有效的。但事实上，即使你参加一天的Session，或阅读一天的文档，都不如和你的Pair实际做一个Story，对知识的分享来得更好。根据我自己的经验，所谓Session或文档的方式，更适合介绍一些领域背景知识，或宏观的系统架构。细节知识必须在实践过程中获得。这时，Pair Programming就显得非常重要了。&lt;/p&gt;  &lt;p&gt;在选择自己的Pair时，也需要针对实际情况做出调整。例如与新手Pair的必须是相对有经验的Dev（当然也可以和QA或BA结对）。另外，也需要适当地Switch Pair。在这个项目中，我就体会到这一点的重要性。第一周的3天时间，和我Pair的都是Andrew。虽然他已经进入项目三个多月，但毕竟还缺乏一定的经验。而我作为项目新人，更是很多内容都不了解。这样的Pair组合，效果可想而知。在这个过程中，本来我们应该进行Switch，但因为Story的关系，其他Pair也有自己的任务，这个Switch活动就被推迟到了第二周。事实上，在第二周的第一天，我和Eric合作，效果就非常好。整个Velocity得到了较大的提高。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wayfarer/aggbug/2239148.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wayfarer/archive/2011/11/07/2239148.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wayfarer/archive/2011/10/18/2216090.html</id><title type="text">设计匠艺模型</title><summary type="text">之前，我曾提出软件设计的七个原则，即重用、扩展、变化（后改为协作）、简约、一致、分离、间接，并在去年的亚太软件研发团队管理峰会阐释过自己的想法。经过这两年的积累，这些内容逐渐丰富起来，而我也根据自己所思所想做了一些调整。我的朋友姜大胡子和刘冰对我提出的这几个原则，表示兴趣和部分认同，但同时也提出一些自己的看法。按照大胡子的意见，一个人的记忆很难记住太多的原则，最好不要超过3个。七个原则好似古龙...</summary><published>2011-10-18T01:29:00Z</published><updated>2011-10-18T01:29:00Z</updated><author><name>张逸</name><uri>http://www.cnblogs.com/wayfarer/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wayfarer/archive/2011/10/18/2216090.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wayfarer/archive/2011/10/18/2216090.html"/><content type="html">&lt;p&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="sevenprinciple" border="0" alt="sevenprinciple" src="http://images.cnblogs.com/cnblogs_com/wayfarer/201110/201110180928419417.png" width="498" height="261" /&gt;&lt;/p&gt;  &lt;p&gt;之前，我曾提出软件设计的七个原则，即重用、扩展、变化（后改为协作）、简约、一致、分离、间接，并在去年的亚太软件研发团队管理峰会阐释过自己的想法。经过这两年的积累，这些内容逐渐丰富起来，而我也根据自己所思所想做了一些调整。我的朋友姜大胡子和刘冰对我提出的这几个原则，表示兴趣和部分认同，但同时也提出一些自己的看法。按照大胡子的意见，一个人的记忆很难记住太多的原则，最好不要超过3个。七个原则好似古龙的七种武器，听起来很拉风，其实会让人难以记忆。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;通过反思这几个原则，我固然同意大胡子的看法，但让我愿意改进的根本原因，还在于我发现这几个原则其实并不在一个抽象层次上。例如，重用和扩展，其实是我们希望达成的目标，它与间接、分离等原则有着根本的不同。所以，我一直思考着该如何改进这七个原则。之前，我已经初步对这七个原则进行了划分。但我始终比较纠结，例如在“重用”原则中，我总结了一些好的设计理念。譬如说，通过粒度的划分与依赖的解耦，以保证有效的重用。从这些设计理念来看，至少在这个层次上，这几个原则又是平行的。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;上次在北京邦元素咖啡的一次技术人士聚会，大胡子给出了他的一套体系。我很认同他在价值观与原则的一些看法。最近，根据自己的一些思考和深入分析，才发现之前提到的“重用”和“扩展”原则中的设计理念，其实仍然可以通过遵循一致、分离与间接原则来保障。渐渐地，这个模型开始成型了。如上图所示，我将这个简单的模型称为设计匠艺模型。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;整体来看，模型分为左、右二图。左图的核心圈，描述的是“简单”价值观。这意味着我们在设计时，需要时刻体现设计的简单性。这同时也是UNIX编程艺术中反复提到的原则。将其放在价值观中，是希望突出它的核心地位。从这点来看，我这个模型与大胡子的模型完全相同。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;“简单”价值观的外面，是三个主要原则（核心原则）：间接、分离与一致。它们不仅是设计的重要原则，同时也是架构的重要原则，尤其是分离与一致（可以阅读我的这篇博客《&lt;a href="http://www.agiledon.com/?p=441"&gt;软件架构的一致性&lt;/a&gt;》），可以通过关注点分离与架构风格一致性，成为软件架构过程中必须遵循的关键原则。这些原则没有包含“抽象”原则，是因为我认为“抽象”其实是“间接”原则的一种体现。“间接”原则的含义更广泛，事实上，它还能够有效地体现对象的封装。正如我的结论，GOF 23种设计模式中，所有模式都体现了“间接”原则。若有不同意见，欢迎PK。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;右图是我们在软件设计中必须要达到的目标（或者说是尽可能达到的目标），即软件的可重用性和可扩展性。它事实上可以看做是软件系统的质量属性。在质量属性中还有其他属性，例如安全、性能、可伸缩性、可维护性等，但我认为这些属性更偏向于纯粹的架构过程。而重用与扩展则是架构与设计都需要保证的。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;中间的箭头代表设计手段。虽然有很多设计的手段，但我始终认为面向对象设计的最大难点，还是在于职责的识别与分配。这正是我提出“协作”手段的原因。而对于一个好的软件系统而言，和谐的对象协作正是设计的主旋律，也是软件质量的正确保障。图中的三角形表现了“协作”原则的主要内容，包括自治对象、专家模式与场景驱动，中间的一块三角型，则是理想的对象协作模型，即形成好的对象社区。这些内容算是对整个模型的一些补充。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;今天打算抛砖引玉，让大家指出模型的不妥之处。这个模型涉及到很多设计的方法、原则，透视了设计的本质，而这些内容则需要大量的篇幅来描述，而这正是我下一本书的主要内容。我会在今后的博客中陆续给出这个设计匠艺模型的细节内容。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wayfarer/aggbug/2216090.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wayfarer/archive/2011/10/18/2216090.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
