-
编写Story的一周,预示了项目的不祥未来 - [Agile]
2010-06-13
我最近Lead一个QuickStart,第三周编写Story,这一周就像是整个项目的缩影,它让我看到了项目的未来:屈服于压力,不尊重实际的速度,迷信业务的承诺,轻信自己的判断,后期加班,项目延期。
我和一个BA想用一周时间(本周有7个工作日)把Story都做出来,并制定交付计划。之所以是一周,是因为第二周要做汇报。开始比较乐观,认为Story可以很快产出。
周一:
只做了30个Story,比预期的少了近一半,但一想只花了半天做Story,以后应可以补回来。周二:
做了28个,远少于预期,因为今天又有别的事,不能全力做Story,也许明天会好些。周三:
做了34个,今天多了些,但感觉Story的总量会比预期多不少。我们反思了一下,业务(客户)的支持不足是最大问题。心想,如果业务一直在身边,写起Story肯定神速。于是争取了一下,业务承诺每天10点至少支持我们2小时。周四:
业务如约来支持我们,但主要是Review做好的Story,没写新的。下午业务开会,还是我们自己写Story,今天完成40个。周五:
业务没有来,因为他每天都工作到凌晨3点,虽然承诺10点到公司,但11点还在自家床上呢。看来只能加班适应他的时间了。今天做了33个。周六:
我们11点多才到公司,就是准备晚上陪业务加班。加上加班的2个多小时,共写了42个,并没有预期的50~60个那么多,原因是“软柿子”都被我们捏过了,剩下的都是复杂的需求了。周日:
一早我和老大分析了一下,决定推迟汇报的日期。业务那哥们今天大部分时间都在支持我们,但由于剩下的都是复杂的逻辑,今天只做了40多个。一周回顾:
一开始根据汇报的时间点做了乐观的估计。3天后有了实际的“速度”,但迫于压力没有据此调整预期,因为还梦想着业务会支持我们并大大提高生产率。后来发现业务近两天不可能提供全面支持,于是决定加班。之后又发现即使业务支持了,速度也未必明显提高。最后决定延期交付。哈哈,整个过程俨然就是将来项目的缩影,该犯的错误是一定要犯地!
-
试用了一下Team Foundation Server中的版本管理功能 - [Version Control]
2010-04-27
今天在Visual Studio 2010内试用了一下Team Foundation Server中的版本管理功能,总的感觉是“痛苦”,其实真想找找它的优点,可能是时间花的不够,确实没找到。要声明一下,这个感觉完全取决于你以前用的是什么版本管理工具以及你有多依赖版本管理工具。
我大部分的项目使用SVN或GIT,曾经有一个项目在客户那里用 ClearCase,大学涉世未深的时候也用过VSS。我在项目中是较强地依赖版本管理的,也就是说该用工具的时候就用工具,而不是通过“文件重命名”、 “目录拷贝”、“收发EMail”、“重复修改”、“共享目录”等手段来做版本管理。
下面总结了我在试用过程中的Pain Point,用过SVN或GIT的人会更有体会。
* 断网了你就死定了打开项目要连接服务器。比如断网时修改一个文件,check out动作需要连接服务器,IDE就挂在那里,直到我受不了了把网线插回去IDE才有反应。如果一直坚持下去,据说会出现警告框,提示文件将脱离版本控制之类的,没有机会验证。
* 悲观的Conflict策略即使修改了文件不同位置,在get latest version后也提示Conflict。
* 乐观的提交一致性保护策略Check In时如果Local Copy不在最新版本之上不会被阻止,而是在某一个文件有Conflict时要求在本地解决,再check in。也就是说会有服务器端的冲突检测,可惜此时服务器上便产生了一个和任何本地版本都不一样的版本,天晓得这个版本会不会工作,在一致性保护上实在是够乐观的。
* 服务器端Diff即使是查看一下本地修改也要连接到服务器才能做到。
* 本地文件控制策略不透明,不开放,而且具有侵入性不像SVN或GIT,所有秘密全在.svn或.git隐藏文件夹内。TFS可以确定的是solution和project文件夹中各有一个版本管理元文件,所有被管理的文件会被“侵入式”地变为只读(在IDE外被更改后会欺骗版本控制,类似ClearCase中的Hijack),再就是通过Source Control Explorer管理了workplace和local path的映射......。
如果把项目文件夹挪动位置或改名,别指望会成功打开,项目会因binding information找不到和workspace mapping错误而脱离版本控制。事实上,你要先创建一个workplace和新的Local path关联,然后TFS才允许你在新的地方签出同一个项目。
本地目录的完整路径是和Server上的目录对应的,这个关系不可以改,项目保存的位置不可以随便变。你创建Branch后也不能把本地目录直接Switch到Branch上,要重新get latest version。
不透明的文件控制策略会让针对版本管理的排错变得困难很多。
* TFS采用了check in/check out/lock的同步模式它并不跟踪真实的修改,即使check out后什么都没改,文件还是修改状态,还需要check in。
* TFS与IDE的秘密约定导致在IDE之外进行版本管理相当繁琐比如对目录和文件做版本管理,需要通过Source Control Explore进行繁琐的操作(与SVN或GIT在资源管理器中的操作相比真是......)
* 有些操作太复杂,蹩脚比如查看本地的所有改动,只能一个一个文件地看,通过右键菜单,每次都要到服务器端做Diff。
再比如Revert Change操作,看看MVP给的步骤吧:https://msmvps.com/blogs/vstsblog/archive/2009/01/10/how-to-revert-changes-checked-into-tfs-version-control.aspx
再比如把一个文件从solution中移出,但想保留版本控制,TFS认为你要删除该文件,为你创建了一个delete的pending change,你需要undo这个change才行,或者执行这个删除,再到Source Control Explorer中把它加回来,别扭死了。
还有到处都是对话框,即使你每次都做同样的事情,还是需要多次点击和选择。
* TFS内部集成了任务跟踪,Project Portal,Planning,Reporting等功能这对大型团队来说确实省了不少事,但对中小团队,唉,又被绑架了。它的敏捷项目管理模板给人官僚的感觉,刚起步的敏捷团队最好是从Story Wall开始。这种集成性降低了灵活性,剥夺了选择的权利,甚至让人懒得去看看外面的世界是什么样的。
* 安装、配置、维护繁琐你需要几个G的空间安装相应组件。
* 只支持http和https在网络条件不佳的环境中速度会有影响
总结
TFS为了照顾VSS的客户,同时眼睛还总盯着另一个官僚版本管理工具IBM的ClearCase,最后搞的它和ClearCase一样,功能不少,很强大,就是思路偏了,适合给大领导看,不适合给团队用。
本来点5次鼠标,打开关闭1个窗口就能做的事它要点30次鼠标,确认3个对话框,打开并关闭4个窗口才能做到,同时你还要不断提醒自己到了哪一步了。它压根就不理会程序员此时到底想干什么。
-
Share DOM Fixtures between JavaScript Unit Tests - [Javascript]
2009-12-21
For the JavaScript unit tests that operate DOM, it's quite handy if the DOM fixtures could be shared between tests. This involves how to load another HTML file from one HTML file for most of the JavaScript testing frameworks (like Screw.Unit and JSUnit), the prior file is the DOM fixture and the later file hosts the test codes.
This blog demonstrates a way to do it through jQuery synchronized Ajax call. Below is the code skeleton of a test file:
<html> <head> <script type="text/javascript" src="lib/jquery-1.3.2.js"></script> </head> <body> <div id="fixture"> <script type="text/javascript"> $.ajaxSetup({ async: false }); // Ajax will run in sync $("#fixture").load("fixtures/DOMFixture.html"); // replace inner html with loaded content </script> </div> <script type="text/javascript"> // tests go here </script> </body> </html>This works for all the mainstream browsers including Firefox and IE.
Another possible approach that I haven't verified in detail is that 1) create XML document element dynamically, 2) load fixture file to the element synchronously, then 3) append the element to the place holder DIV in test file.
-
Manage Mock Dependencies of Controllers with IoC Container - [Test]
2009-09-09
Controllers of a web app usually need the dependent services being injected through constructor via an IoC container. In order to test the controllers, many dependent services need to be mocked, and service calls stubbed.
In most cases, only few of the mocked services are really important for a specific test, and for other controllers, we duplicate similar setup routines in different test classes. These duplicated routines lower the readability of the tests.
While working with Raymand days ago, we tried to manage mock dependencies with Unity container. Here demonstrates the way we used.
ControllerDependencies manages all dependencies used for controllers.
public class ControllerDependencies
{
private readonly IUnityContainer container;
// named mock services are ready for setting up further expectations in test
public readonly IPolicyService PolicyService;
public readonly IQuoteService QuoteService;
// other services
public ControllerDependencies()
{
container = new UnityContainer();
PolicyService = MockRepository.GenerateMock<IPolicyService>();
QuoteService = MockRepository.GenerateMock<IQuoteService>();
// create other mocking instances
container.RegisterInstance<IPolicyService>(PolicyService);
container.RegisterInstance<IQuoteService>(QuoteService);
// register other mocking instances
}
// create a controller instance for test
public T Create<T>() where T : Controller
{
return (T) container.Resolve(typeof(T));
}
public void VerifyAll()
{
PolicyService.VerifyAllExpectations();
QuoteService.VerifyAllExpectations();
// verify against other mock instances
}
}
Below is a test.[Test]
public void SomeActionShouldDoSomething()
{
var dependencies = new ControllerDependencies();
// setup expectation againt named services if needed
dependencies.QuoteService.Expect(r => r.GetQuote("quoteNumber")).Return(new Quote());
var policyController = dependencies.Create<PolicyController>();
var actionResult = policyController.SomeAction();
// asserts go here
dependencies.VerifyAll();
}
ControllerDependencies is reusable for other controllers and could also be the place you put common codes for stubbing the service calls. -
The Waste from Very Fat to Very Lean's Perspective - [Lean]
2009-08-29

Very Fat - Manager defines waste
All what we spend time and resource on are needed, valuable and not waste. Nearly zero waste in out work.
Fat - Oganization defines waste
There may be some waste in our work, like the endless meetings. We admit 15% of our work is waste.
Lean - Customer defines waste
We generally treat what a reasonable customer doesn't want to pay for as waste. A reasonable customer understands why we spend time on testing and refactoring, and is willing to pay for it. Over documentation, unused features, efforts on defects and work in progress are all waste. There is always new waste identified from time to time.
Very Lean - Critical customer defines waste
We treat what a critical customer don't want to pay for as waste. Management, test, integration, refactoring and estimation are unfortunately on the list. They are necessory waste to prevent bigger waste. Doing them frequently, in small pieces, automaticly, early and with shared responsibility are pretty much the guidlines we rely on to minimize them. Sometime we cann't resist challenging everything and feel crazy, maybe we need a doctor at this time. -
When people talk about a software project, they usually think of a car as the analogy in manufacturing industry. With this analogy, somebody believe that it makes no sense to 'translate' practices/ideas from manufactoring world because there are so many obvious differences/gaps between them. This thought also limits us from learning more from TPS(Toyota Production System) practices.
This is a misleading analogy. It's a group of cars designed, manufactured and delivered for a particular market within a period, which are compared to a software project, not just a single car. Below are more analogies:
Software Concept Misleading Analogy in Manufacturing Proper Analogy in Manufacturing
a software projecta car a group of cars designed, manufactured and delivered for a particular market within a period. They may end up with great variety.
a software featurea feature/part of a car a car satisfying a customer
the lifecycle of a software projectthe manufacturing stage of a car a loop including steps of design, manufacture, deliver, after service, feedback, refine design, ...
Given the proper anology above, most TPS practices/ideas become inspiring and meaningful for software development.
And, it is meaningless to compare standard parts with standard software components in context of standard, the two industries share the same pursuit of the standard/better ways of adding values and solving problems in fact. -
Selenium Test Powered by jQuery.parcel - [Test]
2009-07-25
This is yet another way of writing selenium test, which targets making 80% of the test codes more expressive, succinct and noise-free.
Problems of the normal selenium tests- type(), select()..., these selenium commands are in detail and wordy for complex form, test is hard to follow.
- test data tends to be weaved into selenium code, hard to be reused in a clear way.
- Thread.Sleep() and WaitForCondition() as a work around for timing issue mess up the codes.
- IDs or element locators are duplicated from the client side and spreaded everywhere in selenium code.
- hard to catch javascript exceptions of the client side from selenium code.
In a word, pure testing logics tend to be messed up with other noisy codes for different reasons.
How it works
jQuery.parcel is a young jQuery extension designed for better javascript encapsulation. One of the key features is getting/setting state of a part of web page through JSON object. When setting state, that part of page is populated with proper events fired just like real user interaction. This feature enables the new way of writing tests.
In selenium code side, model class is created to represent a group of fields in page and the instance of model class is serialized to JSON, then send to selenium RC for page populating through selenium RunScript command. page state goes back to selenium code in a similar way.
A 'Hello World' sample
State getting/setting through jQuery.parcel
Given a HTML snippet:<div id="person"> <input name="name" type="text" /> <select name="age"> <option>please select</option> <option>18</option> </select> ... </div>
Then
$("#person").state();Returns {name: "", age: "please select", ...} as the state of the person div.
$("#person").state({name: "luning", age: "30"});Input fields in person div are populated in proper order and with proper events(click, change, blur,...) fired. The name of the property in state matches the name of the input.
state() can be called on any jQuery object including div, fieldset, input and so on.
Model class in selenium code(C# for the following sample codes)
public class PersonModel { public string name; public string age; ... }
Choose a JSON serializer, compose javascript and run it through seleniumUse any JSON serializer(JSON.Net, JavaScriptSerializer) you like to do the two way serialization.
PersonModel model = new PersonModel{ name = "luning", age = "30" }; string json = new JavaScriptSerializer().Serialize(model);Then, json string is sent to browser for page population.
void Populate(string containerSelector, object model) { string json = new JavaScriptSerializer().Serialize(model); selenium.RunScript(string.Format("$('{0}').state({1});", containerSelector, json)); } Populate("#person", new PageMode{ name = "luning", age = "30" });Code to get state from page
T GetModel(string containerSelector) { var json = selenium.GetEval(string.Format("$('{0}').state();", containerSelector)); return new JavaScriptSerializer().Deserialize<T>(json); } PersonModel model = GetModel<PersonModel>("#person");By now, we see the basic idea of how to write selenium tests with jQuery.parcel.
More than 'Hello World'
Above is a very simple 'Hello World'. In order to write full-fledge tests for complex pages, we need to leverage other features of seting state and create some javascript helper functions for testability. Here are some topics about this.More control on setting state
Options are available as the second parameter of state(), which are useful for testing purpose, they are:
- sync, true will turn off jQuery animation and run jQuery ajax in sync. This is useful if page has many async behaviours, and Thread.Sleep() and WaitForCondition() are no longer needed in this case.
- editable, true will check that the target field is visible and enabled before setting state.
- verify, true will check that the value is actually set as expected after setting state.
- exist, true will check that all properties in JSON object have corresponding fields in page.
The new version setting state becomes:
$("#person").state({name: "luning"}, {sync: true, editable: true, exist: true, verify: true});If any of the conditions is not satisfied, exception will be thrown in client side and eventually captured in selenium code. This is quite helpful for debuging.
get/set state is not enough for all purposesApproximately speaking, 80% of the testing logic is about populating some fields, clicking something and verifying some other fields. 15% can be done by verifying the attributes(not just value) of a field. For the 5% left, we do them as the way we did them before.
Some handy methods are needed in javascript which are used solely for testability. Here are two of them:
- A javascript function returning attributes of a field as JSON is probalily the most useful one. Attributes may include common attributes of a DOM element like class, visibility and enabled, and also some attributes which are only meaningful for some particular type of input(eg. all options of select). Build corresponding class in selenium code and deserialize the JSON to it.
- A javascript function returning all errors of the page is another useful one for testability.
The final test looks like:
With the facilities above, the final test may look like below.
... page.Populate(new PersonModel{ name = "luning", age = "please select", // ... }); page.Confirm(); // click some button var errors = page.GetErrors(); // call javascript function to get errors on page Assert.AreEqual("age should be selected", errors); ... page.Populate(new PersonModel{ fieldA = "A", fieldB = "B", // ... }); var model = page.GetModel(); Assert.AreEqual("some value", model.anotherField); ...
Advantages compared to normal way of writing selenium tests
get/set state rather than detail stepsNew way comes with a high level concept of state/model of page, and is able to populate fields or get state of fields in batch. This makes code more expressive and succinct expecially for complex form.
Minimize duplication of ID and locator
Test depends on field name, and it will notify us if name in model doesn't sync with name in DOM, because exception is thrown if name doesn't match any DOM element.
Minimize the housekeeping codes
Thanks to option sync of setting state, which makes test free from noisy housekeeping codes like Thread.Sleep() and WaitForCondition().
Quicker
Populate with state results in less round trips between selenium code and browser.
Don't let off any javascript exception
Javascript exceptions are catchable from selenium code while populating page, like exceptions in case of error in event handler or ajax callback, field name typo in model, setting state against invisible or disabled field or failing to set state to expected value. These help developer locate problem quickly.
Easy to manage reusable test data
Test data can be expressed as a model instance or a JSON string, and is easy to be extended or inherited. It can also be stored in txt file and deserialized into code easily. This is expecially useful for reusing test data of complex form.
-
Mark Needham just posted a blog here described the 'waiting for jQuery ajax call' problem in selenium tests and the way we solved it.
The basic idea is recording the count of active ajax requests in client side js code, and generically decorating the calls(click, type, select etc.) in selenium tests with the logic of waiting for the count to be zero.
Ajax request may not be the only async point we want to wait for its completion in selenium tests, animation is another one we care about, but just turn off it in tests so that we don't need to worry about it any more. "jQuery.fx.off = true" will turn off jQuery animation.
Is there any more async points? probably YES. Acturally, what we really want to wait is the completion of any action which is delayed to be executed by window.setTimeout() or window.setInterval(). Ajax and animation rely on window.setTimeout() or window.setInterval() to simulate the async behaviours eventually.
Please check out the code at mark's post or here(with comments) for details. It works for the cases you fire multiple ajax requests at once or fire further ajax request in previous ajax request's callback.
Given this, we removed almost all noisy thread.sleep() and waitForCondition() codes in selenium tests! The build is speeded up and the tests become more concise, readable and stable.
This approach is simple, stable, side-effect free and able to be ported to other ajax framework easily.(jQuery Ajax provides proper events we can rely on, for other ajax framework without build-in events, we can try method delegation instead)
Waiting for ajax call is a common issue in selenium tests, try this approach and make your selenium tests cleaner.
-
Code Snippet to Log All Events as They Are Triggered - [Javascript]
2009-04-28
I paired with Josh Price wrote the javascript code below, just before that we noticed something abnormal about event handling and wanted to find out what happened.
Just paste the code below into Firebug console of Firefox, run it, do something triggering events in page and watch the log printed out.
Please note that this requires jQuery.
var counter = 0; $("*").each(function() { var events = $(this).data('events'); if(events) { $.each(events, function(type, handlers) { $.each(handlers, function(i, handler){ handlers[i] = function(){ handler.apply(this, arguments); console.log(++counter, type); }; }); }); } });
You can add more logging per your need, e.g. the time used for each handler call. -
A Quick Check of Javascript Code Quality - [Javascript]
2009-03-30
Here is the way we used today checking the quality of client side javascript code.
Launch Firefox, access the site under developing, open Firebug console and run the scripts below:
for(var property in window){ console.log(property); }
We get a list of properties directly defined in window instance, which is the place where all global functions and variables exist.Filter out the build in properties and the ones introduced by the external libs, like jQuery, Prototype, and we get all the global functions and variables defined by the team.
Then ask:
- Do we have too many of them and is it a sign of lacking encapsulation?
- Are all of them worth being introduced with global accessibility?
We really got some clues of refactoring, cool!The key is that try object oriented javascript before the plain global functions and variables run out of control.
- Do we have too many of them and is it a sign of lacking encapsulation?
-

My Object Mother is easily messed up with overload methods
Object Mother is usually used as a test fixture to create objects for testing purpose. e.g.:Contact contact = ContactMother.CreateContact();
A typical simple contact will be assembled through the call. As project goes on, ContactMother may probabily looks like below:
CreateContact(string firstName, string lastName)
CreateContact(string firstName, string lastName, string organizationName)
CreateContact(string organizationName, string email)
...Every reasonable combination of parameters may want to overload the method. The class is messed up and becomes hard to use and maintain.
.NET 3.5 Object Initializer could do a better job than overload methods
Object Initializer is one of the language enhancements of .NET 3.5, you can create object like this:SomeObject object = new SomeObject{Property1 = "foo", Property2 = "bar"};
It's a short version of creating object with default constructor and setting properties as your needs.
You may have the feeling that we could create our parameter combination with this much flexible manner.
Create an Object Builder which holds properties used for creating objectpublic class ContactBuilder
{
public string FirstName {get; set;}
public string LastName {get; set;}
public string EMail {get; set;}
public string OrganizationName {get; set;}
// other customizable properties may be used by tests
public ContactBuilder()
{
// set all to default value
FirstName = "foo";
LastName = "bar";
EMail = "foobar@abc.com";
OrganizationName = "ABC";
// set other properties...
}
public Contact Build()
{
// create dependent objects including Organization
// assemble the final contact with the properties and return it
}
}Then, contact can be created with any parameter combination:
Contact simpleContact = new ContactBuilder().Build();
Contact foobar = new ContactBuilder{FirstName = "foo", LastName = "bar"}.Build();
Contact contactWithOrganization = new ContactBuilder{OrganizationName = "Freeway"}.Build();
...Combine with a Call-Chain style builder
Typically, Object Builder build object as below:Contact contact = new ContactBuilder()
.WithName("foo", "bar")
.WithEmail("foobar@abc.com")
.Build();The trick is obviously returning 'this' in all WithXXX methods.
The object initializer style builder is very similar as the call-chain style builder, and some time it's meaningful to combile these two. e.g. if you want to add common behaviours into builder as well, like this:Contact foobar = new ContactBuilder{FirstName = "foo", LastName = "bar"}
.AttendAMeeting("Sales Meeting")
.Build(); -
看过《丰田现场管理方式》,30年前出版的书 - [Lean]
2009-01-22
书的全名是《丰田现场管理方式 - 丰田巨额利润的秘密》,日文翻译的,1978年出版,整整30年了。阅读过程中确实能感受到那个年代的“朴素”,而现代介绍精益生产的书则充满了“提炼与升华”。
该本介绍了丰田生产方式的方方面面,浅显易懂,而且介绍了很多工厂的具体实践,会把你带入生产第一线。照例,下面我把书中一些有意思的地方摘录下来。- 商业活动中,购入物品被加上差价,形成销售价,...,企业收益并非来自于对消费者有意义的增值活动,而是把负担转嫁到消费者身上。这是一种反社会的现象。...
-
Make visible both progress and smell - [Agile]
2009-01-05
Burn down chart and story wall are often used in agile project for
visualizing the status and progress of project. They should be designed
to visualize smell easily as well.Smell is sometime made less visible unintentionally. We probably did things as below:
- Enlarge the wall for more cards.
The wall then became more 'smell tolerant'. Problems are hard to identify.
- Categorize bugs so that we can manage more.
Feeling of bugs being well organized weakens the strength of the will to fix them soon. The count of half done stories may increase with the bugs.
So, in order to resist the tendency, we can try:- End-to-end evaluating, keep goal in mind.
- Make it brittle for even small problems. Here 'it' fits for process, story wall and more.
- Keep it simple, less is more.
- Enlarge the wall for more cards.
-
利用客户端Javascript代码简化Selenium测试 - [Test]
2008-12-23
这是一个基于JSF的WEB应用,被测的场景是编辑一个巨大的表单,然后进行预览操作,最后再回到编辑页面,验证各个字段是否和以前的一样,据此间接检验各个页面元素与后台Bean的绑定是否正确。
以前的测试不是人读的
这是之前实现这一测试的代码片段:
@Test
public void createFullRequest_priview_backEditing_checkData(){
goToCreationPage();
browser.click(ADD_APPOINTMENT);
browser.click("lorryTrucking1");
browser.select("containerSizeType1_1", "label=20' Platform");
typeAndBlur("containerQuantity1_1", "1");
browser.select("cargoPackageType1_1", "label=Bar");......
-
Comments on Agile Practices from My Recent Project - [Agile]
2008-12-20
Story wall- Less is more. Keep wall small and concise. The wall for our project is too big.
- Be sensitive to the wall's smell.
- Move cards under the rules your team currently follow.
- A real wall is better than online system in many aspects. It's more visible, complexity/smell amplifier, team focused, free formed and has just enough functionalities.
- For a distributed agile team, an online system serves a key role.
Story Estimation
- Be aware that under estimation brings the feeling of hurry and tends to lead to less tests and poor design.
- Be aware that under/over estimation contributes ruining the predictability of stories based plan.
- Re-estimate if the original estimations are out of date considering new knowledge gained about the story(requirement or implementation).
- Don't blow up estimations by considering technical debts much. Treat technical debts actively so that we need not to pay much 'interests', and the relativity is there between early and later stories.
Story Planing- Story being played should be end-to-end testable, if not, try putting necessary story in the same iteration. E.g. playing draft page story earlier could make the binding testing of creation page story more doable.
- Play stories around one page one by one, not at the same time. This brings less conflicts, less pain of fixing previous stories' issues, earlier end-to-end testability and facilitates evolutionary design. E.g. stories of creation page.
- Story, bug and tech-task should be treated equally. No need to categorize them separately in 'ready for dev' column on story wall.
Quality
- All roles share the responsibility of quality.
- Build quality in the process how a story is produced.
CI- Light, sound, monitor are all candidates for indicating build status.
- Long local build will stop frequently checking in. If this happen, try to optimize build process, then optimize time consuming tests, then run only a subset of tests in local.
- Run in local build the UATs which are most likely to fail recently.
- Run in local build the UATs which ensure critical features. As smoke tests.
Test- Leverage jsunit tests for client side logics. This reduce the need to write selenium tests which have long feedback cycle compared with jsunit tests.
- It's unpractical to write UATs for all logics. Consider the priorities as below:
- Logics which are most likely to fail.
- For repeated manual testing procedures. As smoke tests.
- For bugs.
- For individual stories.
- Try to refactor your design if you feel it's hard to write unit test.
- Try to refactor your design if it's hard to understand a test.
To DEVs
- Be sensitive when a larger design is needed, initiate group discussion at this time and do just enough simple design.
- Simple design means every guy knows every piece of code.
- It's an opportunity to refine the design when you revisit the same piece of codes. Do evolutionary design.
- Don't write code for sth. you don't fully understand, go to BA at this time, otherwise you may mis-model sth.
- Keep priority/value in mind, be sensitive to the opportunity of spliting a story into two when playing a story. The derived story cost much with less value, could be planed in later iteration.
- Feedback your feelings about a requirement to BA, it's an opportunity to build a new mutual understanding.
- Observe carefully how QAs test a story in mini showcase.
- Respect QA's feeling about the quality, refactor bravely around the area with poor quality.
To QAs- Be aware that the INNER quality can only be enhanced by improving the design of code directly or indirectly. Working closely with dev is the key for high quality software.
- Try to enforce the sense of quality for devs by finding bug quickly and feedback on time.(mini showcase, quick feedback after dev-complete)
- Provide feedback of your feeling about the bad quality of some part, discuss with devs for potential big refactoring around that.
- Let team know the open/critical bug count and bug fixing progress in standup.
- Work with devs closely writing UATs and refactoring them.
- Ask 'does anybody know what allowed the error to occur?' instead of 'who made the error?'.
To BAs
- Make sure devs have the same understanding as you about the stories, otherwise devs may mis-model the domain into code and introduce bugs eventually.
- Be aware that some detailed requirements emerge after start playing the story. Story is negotiable when playing.
- Respect DEV's feeling about a requirement, treat it as an opportunity to achieve a new level of mutual understanding.
- It's an opportunity to simplify a requirement or identify a mis-defined requirement when dev complains it's too difficult to implement a story.
To Team Manager
- Every time you tell people what to do, you take away their sense of responsibility.
- Drive team by prioritizing story/bug/tech-task.
- Enabling members instead of managing them. Coaching over managing.
- Let team know 'all' what you know through an on time and visible way.
- Visual control. Leverage burn-up chart, story wall and more.
- Self-management. Be provided enough information for self decision making.
- 'SLACK' time is the investment to the creativity. Learning, sharing, innovations and breakthrough improvements may come from 'SLACK' time.
Stand-up Meeting- Begin at a fixed time. Don't always make unnecessary delay.
- Make sure problems raised are always followed by action or owner.
- Try attending other team's standup.
Retrospective Meeting- Focused on a higher level reflection on the way we have worked, identifying key problems in project and process, instead of discussing and solving detailed problems, which should happen after daily standup.
- Doable actions with owners are key outputs.
- All members should take responsibilities on long term improvements. The owner should find ways "pushing" the team and keep an eye on the improvement.
- The owner may not be the doer. Every member could be the owner of every kind of action or long term improvement.
- Hold at a relaxed time, in a safe environment.
- Chaired by a guy outside the team if possible.
- Hold not so often. Remember it's not designed to solve daily based problems.
- Try different forms.
Daily Session- An ideal daily session comes from this week and serves mainly on next several weeks.
Spread Agile into Other Teams- Be patient, try and see what happens.
- Group an Agile Promotion Committee consists of Agile experts and a sponsor providing authority support.
- Start with small, step by step. Avoid trying to push all practices to all teams in a unified way.
- Avoid building any standard. Team decide on their own.
-
《系统思考》一书“亮点”摘录 - [Lean]
2008-12-18
书的全名是《系统思考 - 学习型组织必备读本》(<Seeing the Forest for the Trees - a Manager's Guide to Applying Systems Thinking>)
系统思考简言之就是强调从整体、长期、动态和连续的角度认识事物和解决问题,而不是反应式地,局部地、短期和静态地思考问题。它专长于认识和把握复杂系统,揭示和分享团队成员的心智模式。
之所以看这本书是因为我认为“系统思考”(systems thinking)以及“系统动力学”是精益思想(lean thinking)背后的理论支持和思考工具。在这里把阅读过程中发现的一些“亮点”摘录下来备个忘:- 系统思考即整体思考,比较接近中国传统思考方式。在中国人的社会里,当你拿到一个物品时,大多问这是哪里来的;而在西方人的文化里,他们会问这是由什么组成的。
- 企业被分为几个独立部门,高级主管们整天关在会议室里......,技术部门......,质量部门......,各职能部门相互扯皮......“各司其职”的“后遗症”使我们几乎丧失了关照整体的能力,“过度分工”的结果是工作越努力,公司的利润就越低!
- 很多人存在这样一个逻辑,即认为只要每个人、每个部门都把目标达成,整个公司的目标就会达成。所以,只要公司目标没有达到,人们就会自然而然地反推一定是某个部门或某个人没有做好,只要将这个“害群之马”揪出来换掉,问题就会迎刃而解。只是真正的害群之马很难找到。扯淡!造成问题的关键是人们把整体简单当成部分的总和了,并由此导致对部门间互动和关系的忽略。
- 整体大于局部之和。
- 从根本上讲,管理只有两项工作:“踩下油门”(成长引擎)和“松开刹车”(消除障碍)。
- 对于很多团队而言,“我们可以一起唱歌、一起跳舞,却不能一起思考”。
- 系统的关键特征:
- 涌现:系统作为整体表现出来的个体不具备的特性。
- 自组织:动态的,高度有序的,开放的。
- 反馈:大量内部反馈机制。
- 能量流:做为开放系统需要持续注入能量,能量流过整个系统。
- 自修正:大量反馈回路的交互作用保证系统一定程度上免受外部冲击影响。
飞行的鸟群、骑车人和自行车构成的系统、人本身、地球、合格的团队都是系统的例子。
- 系统的精髓就是它的组件之间的连接,而不是作为独立个体呈现出来的各种特性。
- 每位经理都尽职尽责地管理好自己的“一亩三分地”,然而,所有人都成功管理好自己分内的事情的结果,却通常是局部最优化。在大多数组织中,这通常都是组织结构和局部绩效评价措施的必然结果。......工厂经理们受到提高生产效率的激励,把车间填满成品,从而使之变成“市场销售的问题”。
- 理解动态复杂性并清楚地认识到其背后的模式及因果关系非常困难。从某种意义上说,人的思想确实更适合处理细节复杂性,即对某一时间某一地点的系统进行理解,尽管这时的系统由很多元素组成。
- 系统思考和系统循环图帮助你看到复杂现象背后的简单因果回路。
- 有时候“治标”确实比“治本”更有诱惑力。
- 在系统循环图中,是事实在说话,而不是我们的测量能力在说话。因此,只要它确实存在,就把它捕获并记录下来,尽管它可能是一个像“士气”这样的“模糊变量”。
- 就业务中的事务这一层次进行交流,无法达到创建高绩效团队的目的。我们必须进行更深层次的交流 - 必须在心智模式的层次上交流。系统循环图是实现这一层次交流的重要工具。
- 从个人风格和信念两个角度对计划方式作一个划分:

- 那些相信自己可以预测并控制未来的人是“上帝”。他们踌躇满志,知道一切问题的答案。他们根本不需要任何计划方法--他们就是有决断力。
- 那些希望授权但认为他们能够预测未来的人是“赌徒”。他们明白自己必会每注必赢,因此他们希望了解成功的机会有多大。赌徒们很欣赏财务分析。
- 那些希望授权但认为他们能够预测未来的人是“学究”。他们偏爱分析、方法、数据和技术,他们总在寻找“正确的”答案。他们是受欢迎的咨询顾问,总会有人愿意为最新的“管理潮流”买单。
- 最后一角留给“向导”。和学究不同,他们不相信仅仅因为掌握了正确的技术就能自然地发现“正确的”答案、相反,他们知道未来无法预测,他们试图引导自己的组织在各种不确定性间穿行。
- 系统思考即整体思考,比较接近中国传统思考方式。在中国人的社会里,当你拿到一个物品时,大多问这是哪里来的;而在西方人的文化里,他们会问这是由什么组成的。
-
敏捷和精益对我日常生活观点的影响 - [Lean]
2008-12-17
前些天,客户的一个经理问我,来到ThoughtWorks前后自己有什么变化,当时说的都是和工作相关的,今天突然想记录一下自己在日常生活中观点的变化,这些都直接或间接地受到敏捷和精益思想的影响。
- 以前习惯每周坐车去大超市买一大堆吃的回家,买的时候都挺新鲜,吃得时候要留意一下是否已经烂了。现在更多是在楼下的小超市买东西,哪怕会有点贵。
- 以前溜达到超市库房,看到工人们忙碌在堆积如山的商品前装卸,觉得挺有意思。现在心里却想,老子付的账还要给这库房买单,还要给“装箱”、“拆箱”这类无聊的动作买单,心里不甘。
- 以前心理想着“十年磨一剑”,等我牛了之后吓死你们。现在琢磨出点东西就想到处去显摆。
- 大学四年,尽管大部分知识在工作上都用不上,但觉得这些知识对我应该是有些潜移默化的影响的。现在看来,这种想法就是扯淡,是给目前的高等教育找借口,学习本来就是一辈子的事,需要啥就学啥,用不上的东西就是浪费。
- 以前喜欢用大钱包,有用没用的都往里装。现在用一个极小的钱包,除了钱,只有两张卡和一个省份证。
- 以前攒一周的脏衣服,周末一起洗,现在天天都想洗,只是后悔买了那个5.5升的洗衣机,太费水了,很多只能手洗了。如果能有一个小的洗衣机,然后和邻居共用一个大的就完美了。
- 以前搬家的时候这个不舍得扔那个不舍得丢。最近一次搬家,东西扔的真痛快。去年春节回老家,真想把家里的“垃圾”都扔了,可是没法说服父母。我爸连我小学时候用的课本都不肯扔,说是要留给我未来的儿子用,汗。
- 以前到银行,在超市,深信“排队”是一种美德,现在更多的时候是在想“为什么还要让我排队啊”。
- 以前总觉得北京这三环四环不够宽。现在感觉很多路根本没利用上,很多“大院”、“小区”对公共交通一点贡献都没有。
- 以前感觉有个××商业中心、××展览中心和××CBD挺好。现在听到“中心”、“集中”这样的字眼心里就烦,好好的交通就坏在这些中心上了。
我说上面这些变化都和敏捷和精益有关,你信么?
- 以前习惯每周坐车去大超市买一大堆吃的回家,买的时候都挺新鲜,吃得时候要留意一下是否已经烂了。现在更多是在楼下的小超市买东西,哪怕会有点贵。
-
你在项目组中有“安全感”吗? - [Agile]
2008-11-18
回顾会议中的Safety Check 在敏捷项目回顾会议正式开始之前,我们有时会做个匿名的“Safefy Check” ,以了解大家在分享自己的想法时是否有“安全感”。一般分5个等级,1代表“我会微笑,说事情进展的不错,并同意经理的所有看法”,5代表“愿意敞开谈任何问题”。
这样做是让大家认识到,每个人心里的安全感是不一样的,组织者会据此变换会议的形式,以确保在达到会议效果的同时不让任何人感觉不舒服。
项目组中的Safety Check
同样的,在项目组中,每个人也都有自己的“安全感”等级。让我们试着回答下面的问题(每个问题的前一个选择表示你在团队中有较高的“安全感”):
- 你是否愿意及时表达自己的意见,而不必担心会被指为是幼稚的想法。
- 在有疑问时,你是否愿意在第一时间找到对此问题最熟悉的人寻求帮助,而不管他/她是谁,也无需担心被鄙视。
- 在你做一些自己没有把握的尝试时,得到的常常是鼓励与指导,还是阻力或质疑。
- 在你做一个选择或决策时,考虑的是对整个团队是否有价值,还是更在意给项目经理或某一个人留下的印象。
对上面问题的回答可以反映出成员在项目组中的“安全感” 级别。当安全感等级较高时,团队的能动性和创造力可以被充分激发出来,团队成员工作起来也开心,幸福。相反,较低的安全感让人感觉压抑,要处处小心。“安全感”是主观的,无法强加的,也是团队建设的重要方面。你在项目组中有“安全感”吗?
-
将客户端逻辑封装进Javascript对象,并用Jsunit做单元测试 - [Test]
2008-11-04
最近的一个Web项目大部分的逻辑都是客户端页面逻辑,主要是通过Javascript实现的,如何进行高覆盖率的单元测试是我们面临的一个问题。
分 析一下,有两种逻辑需要测试,一个是与服务器交互的页面操作,比如提交表单后跳转到另一个页面,这很适合写Selenium测试。这里关注的是另外一种, 即客户端页面逻辑,它与服务器没有交互,比如页面Dom元素的联动变化和数据校验逻辑等,这部分当然也可以写Selenium来测,而且由于不与服务器打 交道,组织得当的话速度也很快。但此时Selenium测试与代码实现的反馈距离太远,不方便组织细粒度的测试,更重要的是写这个测试对 Javascript代码的设计没什么帮助,因为再烂的Javascript实现可以方便地写出Selenium测试。
对于客户端页面逻辑,我们尝试使用Jsunit进行测试。对于没有Dom操作的纯Javascript函数,测试很容易。对于有一定复杂度并有Dom操作的逻辑,我们将他们封装成Javascript对象并用Jsunit测试它。
一个具体的例子

上图是页面的一部分......
-
谨慎地创建和使用工具类 - [Software Development]
2008-11-02
Utility, Helper, Common常被用来给静态的工具类命名,我一看到这样的东西就不由地皱眉头,不是说工具类没有用,而是因为看到太多工具类的误用了。下面是我在工作中遇到的两个例子。有一次,看到下面的Java代码:
public void doSomething(){
if(StringUtil.isEmpty(this.value)){
// do this way ...
}
else{
// do that way ...
}
}代码使用了StringUtil,觉得奇怪,难道内建的String功能不够强大,还要搞出个StringUtil?直觉告诉我,isEmpty是多余的,一定是哪里做错了什么......
-
这是次贷危机的根本原因吗? - [Economics]
2008-10-18
刚刚看了张五常老师的一篇博客,他通过一个浅显的例子,借助分析“借贷/资产比率”解释了次贷危机的原因,我总感觉不足以令人信服。他的例子我总结了一下大致是这样的:
房价100万,按揭70万,此时“借贷/资产比率”是0.7,安全。如果放贷人拿着借据再向第三者借50万,市价100万的房子总借额是120万,“借贷/资产比率”是1.2,高于1,不安全。如果各方正常付息,则相安无事,但一旦房子所值不足,还款链条断裂,就麻烦了。这种按揭之上再按揭的做法,包括衍生品打包出售,会使得借贷比率升得很高,杠杆很大,最终形成危机。
这段描述有个问题:第一个放贷人...... -
重构泄露的逻辑而不仅仅是修bug - [Agile]
2008-10-12
修bug时我们都有这样的经历:简单查看了一下代码,发现没有那个类负责这个逻辑,进一步深入代码,查找一番,才在一个看似不相关的地方找到了错误的根源。原来这个逻辑被散落在几个地方,隐讳地错误实现了。我们在出错的地方多加一个if else,或是写点别的什么代码,这个bug就这样被fix了。仅仅这样做虽然提高了软件的外部质量,但对内部质量的提高毫无益处,泄漏在外的逻辑不仅仍然无家可归,而且有变得越加复杂和晦涩的倾向。
每一个bug的出现都是对分析、设计和编码过程的一次质疑。这次......
-
Q:单元测试的主要作用有哪些?

A:单元测试的作用主要有:
(1) 确保代码实现了预想的逻辑。这是所有测试都有的,也是最重要的功能。
(2) 确保重构不破坏现有的功能。所有测试都或多或少地支持这一功能,有些“重构不友好”的测试在重构过程中可能会被删除或修改。
(3) 驱动出设计,主要是帮助识别出类或接口函数。这是个一次性的好处,只有部分测试会起到这个作用。那些直接保证内部函数实现逻辑的测试起不到这个作用。
(4) 当难以编写满意的测试时,提示开发人员现有设计的问题并鼓励他们重构现有的代码。这也是个一次性的好处,只有部分测试会起到这个作用。
(5) 作为需求文...... -
在InfoQ上发表了《软件开发中的准时化生产》 - [Lean]
2008-07-12
前些天在InfoQ中文站上发表了《软件开发中的准时化生产》一文,它是在我的一篇同名博文基础上修改而成的。李剑指出了原文中的很多问题,并给了我不少建议,新文章在内容、结构和语言上有了不少改进。
文章简介:
准时化生产(Just In Time,JIT)是精益生产(Lean Production)中的重要概念,它来源于丰田汽车公司,强调在合适的时间生产合适数量的满足客户需求的产品。20年前,丰田汽车公司的销售额仅为通 用的一半,而现在,精益生产已经使丰田超越了通用,成为全球最大、最成功的汽车生产商。
由 于制造业和软件开发行业都面临着一些类似的问题,软件开发组织从一开始就借鉴着制造业中不同的生产和管理方式,并形成了不同的软件开发方法。敏捷开发与准 时化生产中的很多观点和实践是一致的,精益思想作为准时化生产的指导思想也正在积极地影响着软件开发的方式,向其中注入着创新与活力。
准时化生产的思想强调:- 在软件开发过程中进行频繁的交付,以此减少库存(部分完成的工作),暴露并消除浪费,加快价值的流动。
- 通过客户来拉动开发过程,确保每一时刻团队成员都在围绕客户认为最重要的软件功能展开工作。
- 在第一时间、第一现场发现问题,并从根本上解决问题,以此将质量内建在分析、设计和开发的过程中,而非依赖后期检测来保证质量。
- 将与项目相关的所有人组成高度授权的团队,通过可视化地向团队成员全面展示项目信息来支持自我决策。
- 当改进流程和实践时,要以快速交付和高质量为首要目标,而成本则要端到端地考量,低成本往往是实现快速交付与高质量的自然结果。
-
【本文发表于《程序员》杂志2008年第7期,是在《频繁的Switch Pair是一种浪 费吗?》一文的基础上修改而成的。】
敏捷软件开发中有一项实践是结对编程(Pair Programming),指的是两个人坐在一起共同完成一项任务,这样做的目的常常被解释为便于共享和传递知识,提高代码质量。
结对的两个人是需要更换(Switch Pair)的,否则时间长了就跟一个人没什么区别了。不同的团队往往有不同的更换策略,... -
想起了准备“敏捷中国”大会演讲的过程 - [Agile]
2008-06-28
《精益工厂之旅》演讲视频
精益工厂之旅PPT
精益 思想与软件开发(简化版)PPT
Lean Thinking & Software Development PPT
精益 思想与软件开发PPT
6月21号,我参加了第三届“敏捷中国”技术大会,做了“精益工厂之旅” 的演讲。其实我一开始准备PPT并不是这个题目,是在大会的前一天中午才决定换成这个题目的。
回想起准备演讲材料的过程,还真是费了些周折。我本来准备了一个“精益思想与软件开发”的PPT ,在公司内部演练的时候,发现时间严重超时,同事反馈说内容跳跃太大,理论和新概念太多。我心里有些没底了。
我又赶紧把PPT拿给马波看了一下,结果被他痛批了一番。他说......
-
精益思维 vs. 传统思维 (Lean Thinking vs. Traditional Thinking) - [Lean]
2008-06-22
精益思维 传统思维
是流程的问题。
是员工的问题。
系统思考,优化整体。各个部门优化自己的工作。
快速交付和高质量是相互促进的两个因素。快速交付和高质量是一对矛盾,而且很难做到。
流程应该“脆弱”一些,小问题会因其破坏性而被及时处理,最终无处藏身。流程应该“强壮”一些,多设置一些“缓冲”和“保险”,让小问题被吸收掉。
针对流程进行考核。
针对个人进行考核。
清除员工面临的障碍并开发员工的潜力。激励并管理员工。
是什么让错误发生了?这是谁犯的错误?
更频繁的预测是改进预测可靠性的有效办法。做更加全面细致的分析是改进预测可靠性的有效办法。
小而灵活才是美。大而集中能提高效率。
少做那些没有价值的工作。加速做那些能产生价值的工作。
我的工作要如何配合其它工作将事情最终搞定。
要了解并做好你自己的工作。
只要理论上可行,我们就试试看会有什么结果。在有实事和数据证明其可行性之后再动手。 Lean Thinking Traditional Thinking
The process is the problem.Employees are always the problem.
System thinking and optimize the whole.Optimize functional departments.
Fast delivery and high quality accelerate each other.Fast delivery and high quality are expensive.
Process should be BRITTLE and easily broken by small problems.Process should be STRONG with all safety switches turned on.
Measure the process.Measure the individual.
Remove barriers and develop people.Motivate and manage the employee.
What allowed the error to occur?Who made the error?
Forecast more frequently to improve reliability of the forecast.Make a comprehensive analysis to get a better forecast.
Being small and flexible is beautiful.Being big and centralized is efficient.
Reduce none value-added work.Speed up value-added work.
How your job fits in the process and get the things done.Focus on your job and just do it.
Let us try and see what will happen if it is feasible in theory.Start changing after we prove that it will win with fact and data. -
Unit Test Make Smelly Code More Smelly - [Agile]
2008-05-20
Unit tests have many benefits, they are usually expressed as:
* ensure existing logics during developing
* drive out design
* documentation of requirements
Another big benefit is making smell code more smeller, brave developer... -
软件开发中的准时化生产 - [Lean]
2008-04-28
准时化生产(Just In Time)是精益生产(Lean Production)和丰田生产系统(Toyota Production System)中的概念,其目的是在合适的时间生产合适数量的满足客户需求的产品。它充分体现了从客户价值出发组织生产运营系统的观点,是一种先进的生产方式,为包括丰田、戴尔等众多世界500强企业的成功奠定了基础。
软件开发组织从一开始就在向制造业借鉴和学习,并形成了各种不同的开发方... -
Thoughts on building the environment for innovation - [Agile]
2008-04-20
Thoughts on building the environment for innovation:
* Don't Vote
Voting does not usually encourage ideas which are not mainstream or attractive enough.
Voting is reasonable only for 2 cases, one is that the voters ha...