不止一次的,我听到身边从事软件的朋友的抱怨:我已经从现在工作中无法获得成就感了;项目上的东西都会啦,有什么新的需求想法闭着眼睛也知道怎么实现;8小时期待完成的工作,6个小时就做完了;下班之后,工作上的事情也没什么好像的,似乎一切都顺水推舟那么轻松和容易。
一些不满足的程序员开始偷偷摸摸搞副业。学习新的语言,或者研究新的技术,自己设计一点小东西,乱七八糟的都来一点。从结果看来,居然还不错——工作没耽误,自己也能够与时俱进的与这个变化越来越快的技术世界保持同步。
我清楚的记得在公司的每一个项目。项目初始的时候一切都是未知,需要花费很多的时间去学习和了解:不同的业务领域,不同的技术体系等等。虽然难,但至少有张力驱使。这个阶段不长,很快,项目中边边角角的东西成为工作常态,工作本身变得不那么有趣起来。这时候,完成工作本身之后,研究相关的东西成为常态。很多有意思的东西都是在这个阶段完成的。例如04年做的Buffalo, 就是在做某个商业智能产品过程中抽取出来的;InWritingRoom(Python, GAE)是在新加坡做的;ycl0(NodeJS)则是在去年做Rails项目做的。这些当然谈不上出色,甚至都谈不上“产品”,但是对于保持对软件的热情和兴趣,以及适当分散主职工作直接注意力都有莫大的好处。当然你也可以发现无数其他的例子,Rework这本书是37Signals在自己运营过程中产生的副产物;Twitter Bootstrap则是自己做产品过程中的结晶;capybara则是一个很普通的商业公司在工作过程中的副产物。
为了验证这个想法是不是具备普遍的通用性,我尝试在自己的团队中进行这项尝试。在项目度过初始的混沌期后(一般是两到三个月),开始“不务正业”,例如让团队每周拿出2小时在一起来学习新的语言技术。我们做过用Scala/Javascript/Box2D的坦克大战,也“尝试”做过用Ruby/REST纯命令行的扑克牌游戏。无独有偶,当我将这些与别的公司朋友交流时,他的团队对此类活动也非常欢迎。没有数据证明这样做会带来多少的生产力损失或者能力提升,但直觉中,创造常规工作之外的共同挑战,对于自我的提升与团队凝聚力,都是有着明显的改善。
传统的管理者似乎妄图控制程序员的每一个脑细胞在工作时间、甚至工作时间之外为雇主做出贡献。最简单直接的做法是无休止的加班;或者相当饱和的工作量。殊不知,程序员自己都无法控制自己的思维,何况外力?另外,也见过一些传统的公司传统做法:将工作内容只是换了一种形式,例如“Find Bug Day”之类,能够激起一点兴趣,但长远看来,与工作关系不大的反而能获得更多的热情。
It seems that every javascript framework of a certain size is compelled to implement some kind of pseudo-classical type system. I am now fairly convinced that there is no good reason for doing so. I’d like to describe an alternative approach to dealing with types in javascript.
This approach comes from a recent project where my team were building a pure-javascript single-page web application. This wasn’t a huge codebase, but due to the single-page nature of the app it did have a reasonable amount of non-presentation logic which was implemented client-side in javascript. We needed a type system but we knew we didn’t want to try and force a classical-OO system onto javascript. The patterns we evolved gave us just enough structure while still allowing us to take advantage of the highly flexible and dynamic nature of javascript.
In this post I’ll be summarizing the major parts of the lightweight, class-less type system we created. Before that I’ll start with a brief tour of javascript, covering language concepts which are relevant to our approach. If there is sufficient interest in this topic I may write this up in more detail here over a series of posts.
Javascript is not a classical Object-Oriented language. Over the years many have tried to force it into that mold, but it just doesn’t fit. In javascript typing is achieved through the use of prototypes, not classes.
In languages like Ruby or Java you have Classes and Objects. An Object is always the instance of a specific Class. You generally define an object’s behaviour by specifying that behaviour in the object’s class (for the sake of clarity I’m glossing over things like ruby’s eigenclass). On the other hand in javascript an object’s type is not determined by its class, but is instead based on the object’s prototype. Unlike classes, a prototype has no special qualities. Any object can act as the prototype for another object. A prototype is not distinct from other objects, nor does it have a special type.
In a classical OO language an object derives behaviour and state-shape (i.e. member variables) from its class, and also from superclasses of its class. In javascript, an object derives behaviour from its prototype and also from its prototype’s prototype, and so on up the prototype chain.
Javascript objects are in essence just a dictionary of key-value pairs called properties (plus a prototype as described above). Public object state is added directly to the object as a property, and object ‘methods’ are simply a function which has been added directly to the object (or its prototypes) as a property.
Storing state directly in an object via its properties would mean that that state is publicly accessible in a similar fashion to a public instance variable in an OO language. This has the same drawbacks as in other languages (uncontrolled access, unclear public interfaces, etc.). To avoid this pitfall a common idiom used in javascript to control access to state is to instead store that state outside of the object itself, but rather as local variables of the function which created that object. Due to the scoping rules of javascript those local variables are closed over by the object, and are thus available for access and modification.
Note that the duck instance we create is not storing its name anywhere inside the object. Instead the name argument passed to the createDuck function has been closed over, and is accessible to that duck instance. Also note that the value of name is therefore private. In fact it is private in a very strong sense. It is impossible for any client of the duck instance to see or modify the value of that name variable. No tricks of reflection or runtime object manipulation can get around this. Interesting sandboxing systems such as Caja have been built up around this property of javascript.
In a typical OO system you would define the types in your system using classes such as Duck, Pond, Food, etc. The orthodox approach in javascript would be to define types using prototypes. In our approach we do neither. We have no classes or prototypes, just constructor functions. Each type in the system has one or more constructor functions which create instance of that type. So we would have createDuck(…), createPond(…), createFood(…).
The closure code sample above shows the general form these constructor functions take. The equivalent of methods and member variables are defined inside the constructor function and at the tail end of the function an object with references to the methods is created and returned.
As mentioned earlier, storing state directly in object properties is analogous to having public instance variables. There are very few cases where this is a good idea. Instead our objects maintain state via closed-over local variables declared in the object’s constructor functions. In this way object state is private and encapsulated but still available for manipulation by the object’s functions, because they are declared in the same constructor function. Access to state is exposed in the public interface of a type instance via these functions which are added to the returned object, becomding ‘methods’ on that instance.
In javascript public ‘methods’ are just functions added to an object as a property. This means you can easily mix in new public behaviour by just merging the contents of the a mixin object with the instance itself. For example:
Here we have a shouter mixin and a createShoutyDuck constructor function which uses that mixin to construct a new type of duck which can shout. The constructor achieves this by using the methods defined in shouter to extend the duck type that is defined by the createDuck constructor function. We often use this pattern as an alternative to implementation inheritance. Instead of a base class which provided standard behaviour and subclasses which layer on additional behaviour we instead would have a base mixin which provides standard behaviour and constructor functions which define types by mixing together that standard base behaviour with additional type-specific behaviour. It’s also interesting to note that as well as using mixins to extend a type via a constructor function it is also possible to extend specific instances of a type with mixed in functionality on an ad-hoc basis. This allows interesting dynamic patterns such as mixing in ‘admin’ methods to a specific instance of a User type once they have been authorized as an admin via a service call.
We follow the standard practice in large javascript codebases of namespacing using the module pattern. Coupled with our other practices, this means that a module will generally only expose a small number of constructor functions to other parts of the system. Inside a namespace we may have quite a few types (and thus constructor functions), but across module boundaries only a few types are shared, and thus only a few constructor functions exposed. This is a natural outcome of following the time-honored practice of organizing modules with low coupling and high cohesion in mind. Global variables are avoided at all costs, and pseudo-global variables (i.e. raw variables tacked on to a top-level namespace) are considered a design smell.
Dependencies are wired together in an initial boot function. Because this function’s role is to connect large modules together it has a side-effect of documenting in a relatively declarative, high-level way how the various parts of the system depend on each other.
Dependencies are generally hooked in by providing constructor functions as parameters to other constructor functions. As well as directly passing constructor functions, it’s also quite common that partially-specified constructor functions are used to encapsulate lower-level or cross-functional dependencies. Here’s another contrived example:
This example shows two distinct applications of currying. First, we have exposed a curried createDuck function to the outside world, with the logger dependency hidden. We also used a curried createLogger function called duckLogger to pass to the createDuck function. Again this was to remove cross-functional implementation details (about logging in this case) which the createDuck constructor should not be aware of or care about.
There is a strong similarity between our use of partially applied functions and the parameterized Factory classes you see quite often in statically typed OO code (particularly Java).
A notable advantage of this dependency injection approach is that modules can easily be tested in isolation by injecting test doubles in place of real dependencies.
A slightly unfortunate side-effect of capturing our instance state in closures is that it is hard to expose that state to mixins and sub-classes, as the state is only available via lexical scoping and is not available via the this keyword. Another way of putting this is that there is no equivalent of the ‘protected’ scope found in a language like C++ or Java. This leads to a favoring of composition over inheritance/mixin when it comes to sharing common behaviour between types. Instead of mixing in behaviour that requires access to private state we would wrap that behaviour in a helper object. That helper object would be available to types which need it via their constructor function. Instances of those types would then call out to that helper object as needed, passing in the private state needed by the helper to provide its functionality. Sort of a ‘tell-don’t-ask’ approach to using shared behaviour.
By following this approach to creating types we’ve found no need for the prototypical type system built into javascript. Our code does not use the new operator at all, except when interacting with libraries or frameworks which require the use of new to instantiate objects.
The lack of new also means usage of the this keyword becomes less frequent. References from a method’s implementation to member variables or to other methods in a type are mostly achieved via lexical scoping. this is only really needed when writing mixins which achieve their work by calling hook methods implemented in the instances they are mixed into - see the shouty duck sample above for an example of that.
I have been honestly rather surprised at how well this class-less approach has worked. I would certainly use it (and refine it) in future javascript projects.
I’d love to hear feedback on what I’ve outlined here. If there is interest I may write some follow-up posts diving into more detail on some of the aspects I’ve touched on here.
高内聚是有极限的. 当代码在一个维度上高度内聚的时候, 在其它维度上是发散的. -- 代码内聚设计的不确定性原理
大家都知道量子力学的不确定性原理: 在微观世界里, 有几对物理量不能同时精确的测定, 包括速度与位置, 以及能量与时间. 比如当我们精确的测定一个粒子的速度使其误差很小的时候, 我们对其位置的测量误差从0到正无穷都有可能, 换句话说, 此时粒子可能位于宇宙的任何地方, 这里的极限就是二者误差的乘积总是大于一个被称为普朗克常数的数.
代码的设计有时会感到同样的张力: 无法做到完全的内聚. 当代码在一个维度上高度内聚的时候, 在其它维度上是发散的或耦合的. 无论是逻辑设计还是物理设计.
当代码在结构上内聚的时候, 在行为上是发散的. 主流的面向对象编程范式是按照结构优化的, 看一下你的代码中的class的名字, 大都是名词, 是一个事物, 表达了What the system is. 但这个系统具有什么样的行为, 能做什么事, What the system does, 却被切片, 分散到系统的各个角落. 我们或许能说出系统的静态结构是什么样子, 却要花费更多的精力才能搞清楚系统做了什么, 能做什么, 结构间的交互是什么样子.
面向过程的编程范式是按照行为优化的. 过程的名字通常是动词, 表达了 What the system does. 系统的静态结构则被掩藏在了行为之后
DCI有助于解决代码逻辑设计的内聚问题.
当我们按照feature来划分目录结构的时候, 相同的技术架构层次的代码会被分到不同的目录中. 比如数据访问层的代码会分散到各个feature的目录中.
当我们按照技术层次架构来划分目录的时候, 同一个feature的代码会被分到不同目录中, 而同一个目录中会包含多个feature的代码. 比如流行的MVC框架的缺省目录结构都是所有的controller放在一起, 所有的model放在一起等等. 当我需要看一个feature的完整实现时, 需要从不同目录中挑出不同文件来查看.
现在的编程语言是基于文本的, 或者其实也是基于文件系统的. 文件系统是一棵树, 每个叶子节点只能隶属于一个父节点, 树的结构只能按照一个维度优化. 语言本身的元数据也不够丰富. 这些都限制了能够生成各种视图的IDE的出现
这应该只是约束理论的一个实例.
作者:chelsea 发表于2012-2-6 22:57:56 原文链接 阅读:191 评论:0 查看评论
I’ve been playing around with CukeSalad: Cucumber specs / tests without step-definitions:
Cucumber, washed and ready to eat for Friction-free ATDD/BDD
In creating an example of my simple WatirMelonCucumber framework using CukeSalad, here’s some of the things I’ve found. Please note: these are my personal observations and are not direct criticisms of the huge effort that has gone into making this tool.
It’s not really no step definitions: they’re just called different things. Instead of writing steps, you’re writing roles and tasks, both of which are mandatory, so I found I actually wrote more code (in more files) than if I just wrote step definitions in the first place.
CukeSalad forces you to use predefined step syntax: which to me defeats the purpose of having features in business language. For example, as far as I can tell, Given statements are purely used for setting the roles, and therefore a business person can’t use it to define a precondition which I would normally use Given statements for. Another example is when you want to capture some data from the test, this must be on the end of a sentence following a semicolon (or comma)
When I search for: the phrase 'Watir'
I spent ages trying to work out why steps don’t match: as you still use Cucumber to run your features, Cucumber will only tell you that there’s no matching steps. This doesn’t help when there’s no steps whatsoever! At some point, I realized that Then steps follow a different syntax, and they can’t use placeholders like Whens, but must have an expected value on the end in single quotes.
Then I should see results greater than '30,000'
It’s very confusing what you’re actually asserting on a Then step: I couldn’t work out a way to capture the expected value from the feature (in the example above ’30,000′), and CukeSalad just asserts the task returns the actual expected result, which doesn’t help if you’re not testing for equality!
It’s very confusing trying to convert an existing Cucumber test suite: I wouldn’t bother. I found I had to completely rewrite my feature to comply to the syntactical requirements of CukeSalad, so I can’t this working on an existing feature set.
Cucumber world is not supported: so just do a normal include instead in env.rb.
Summary
Sadly, CukeSalad doesn’t offer me anything over using Cucumber. Whilst it sells itself on having no step definitions, I found there was greater effort in writing the roles and tasks. I don’t like the way it forces me to write sentences using somewhat awkward constraints, and how Given, When, & Then statements all behave differently. It certainly gave me a lot of WTF moments, which isn’t what you want when writing automated tests.
I’d be very interested to hear whether anyone has had ongoing success with CukeSalad.
During December/early January we spent some time analysing an existing system which we were looking to rewrite and our approach was to look for how we could do this in an incremental way.
In order to do that we needed to look for what Michael Feathers refers to as a seam:
A seam is a place where you can alter behaviour in your program without editing in that place
On previous times when I’ve been thinking about seams it’s been at a code level inside a single application but this time there were more than one pieces interacting.
We knew that there was a web application where the user could request a quote which would be calculated offline and then an email sent to them when it was ready to view.
That led us to believe that there was probably some sort of queue being used to store the outstanding requests and there’d probably be some sort of application processing the requests.
As it turned out the design of the system actually looked like the diagram on the right with the database effectively as a queue.
We then needed to work out which tables we had to read from/write to so that we’d be able to just replace the ‘polling application’ and leave the ‘web application’ alone.
We were then able to come up with a design whereby we isolated any interaction with the database into a ‘bridging application’ which then farmed requests out to a new application which we could scale horizontally.
It could also take care of writing the quotes back into the database so the existing application could read them back onto the screen.
Although we ended up not using this architecture for other reasons I think it’s a neat way of looking at systems to work out how we can change them with minimal impact.
Web applications take effort to develop for the most part. Serving static pages is much easier. Over the years the patterns and techniques to develop web-apps have changed, and so has the division between logic we write on the web-server tier versus logic in the browser. We now write much more logic in the browser, and indeed we’re on the cusp of an era where Model-View-Controller in side the browser is the dominant design. Micro-web-frameworks have made that possible, and there are new problems to solve.
In this blog entry, I outline the difficulties making solid web-apps where the model is being heavily mutated. Earlier today I mulled the technologies in play in 1993, 2000 and 2006 for enterprise web-applications, and this blog entry is intended to continue the cataloging of technologies to the current day.

I’ll phrase this as a status quo for the technologies in question.
Micro Web Frameworks are largely divorced from the back-end server technology that hosts the larger web app. As such, we can consider them interoperble with many alternate backends. In that web-server tier, Rails is still an immensely popular technology, but there are new kids on the block: NodeJS, Grails, Scala’s Lift and Play. Rails has competition with Sinatra. It ate its previous competitor – Merb and Rails merged to form Ruby on Rails 3.0. SpringMVC and the one of .Net equivalents are one more the more ‘safe’ enterprise choices. PHP is still an amazingly popular choice for startups of course.
The frameworks competing in this space include Backbone, Knockout, KnockBack, Spine, Ember, BatMan, Angular, Sammy, JavaScriptMVC, YUILibrary, SproutCore, Broke and Fidel which all ship as JavaScript scripts from 30K and upwards. The genus kicked off in 2009, but the competition did not heat up until the middle of last year. They are all variations on an MVC theme. HTML markup is kept (and the tool-chains and techniques around that). But the page is now able to have programmed series and other turing complete functionality with an absolute minimum of hand crafted JavaScript. The libraries perform something akin to magic to rewrite the page according to JSON data sources and a meld of more functional enhancements to HTML (that the library recognizes), and a spattering of JavaScript for the more advanced functions.
Back in the web-server tier, the chore is now serializing to and from JSON. Some languages have that built in, some require libraries. Java’s XStream falls by the wayside for Java as it is better transforming to JSON than from it, Luckily there is GSON and Jackson. Indeed having classes that support specific hierarchical structures of JSON is the challenge. The dynamic languages have the advantage of course.
The shift in onus is from the model being (mostly) in the web-server tier, to there being no clarity about whether the model is there or in the browser tier. This is the inherent problem right now. A starting position for design (once you’ve decided to go with a micro web framework) is to have the browser tier do all the MVC for the app one ‘page’ at a time, and retrieve JSON from a vestige app-server as well as put it back when done. That makes the back-end more of a document store, and thinner by necessity. It is easy to see that the persistence technologies built to support such apps should naturally retrieve and save such documents rather than normalized sets of records. Old fashioned DB technologies (Oracle, SQLServer, MySql) still have a place, but the direct page-supporting tables should be more like document stores, and have CLOB fields to hold JSON, with simpler key fields. You should be more drawn to the likes of Cassandra, MongoDB, or CouchDB really though.
With these browser-based MVC designs, if the web-app tier is doing any business logic, it is validating data a second time, as one can’t really trust what goes on in the browser with the likes of GreaseMonkey and Firebug around. If it finds validation issues, it is more than likely to send that back to the browser for re-presentation. An easy way to do that would be to embellish the document received with errata, and have the MVC app just display them inline. More about that in a follow up article.
Things get more complicated when a larger document is served to a HTML page, but only smaller sections may be changed at a time. There’s a temptation to POST back the subsection, have the backend validate/persist that, and simultaneously rework the model in the browser to reflect what “would have been served” for the whole document. Why not PUT the whole document to the backend, and reload if needed? If the document is as big as a word document, and your app functions like GoogleDoc’s word processor, then it would die with the overhead of the whole document going up and down per keystroke. Google, has a very efficient wire language to describe the keystrokes being made in a POST operation labelled ‘mutate’. It hints that their model is mirrored to both sides of the wire. It has to be really, as other people can concurrently edit Google Docs.
The GoogleDocs design should make us hope for safety and transactionality with apps that use micro-web-frameworks. The Page could be alive a long time without pushing data back to the web-tier and hence the data storage. A beautiful aspect of MVC is the fact that you can in fact abandon (garbage collect if you like) the model, if the changes it holds is no longer required. The older data-binding style of models is much harder to coerce into abandoning DB writes if required. GoogleDocs allows the rolling back of a series of changes. It feels more like a source control system in that respect. Yet, as long as you are connected, everything is safe, and the chance of loss of data is down to a small number of keystrokes (and for when the browser/machine fails).
By contrast the traditional install of Microsoft Excel means that it’s going to be editing a file-system available XLS document. While you’re editing it, it keeps a backup version just in case it crashes for some reason. You could safely reply on that backup, until you’re ready to for a formal SAVE and close of the worksheet. Your alternate is to close without saving, which is the equivalent of abandoning the changes. With either action, the backup file is deleted. Excel’s metaphor allows for working detached from the network which is handy in for “on an airplane” scenarios.
Is there a model where the web-server initiates a workflow for ‘record’ of some sort, where work is ongoing on that record, and will be completed later? Maybe the record is locked for the duration. maybe not. That would be ‘work-in-progress’ and the representation of it should be mirrored like in GoogleDocs. All changes to it would be safe. You also get the late “submit” or “abandon” reality of a detached model. As it would be document-centric, there could well be a “diffs” that describe the modifications from a starting position, or from last advice. That loses some of the intent of the changes being made though. Martin has an article from seven years ago on Event Sourcing that is well worth a read. Event Narrative is a follow up. Martin is much clearer on the chronalog of changes made to a model, during an implicit save/publish phase. Would the micro-web-frameworks grow a synchronization side that can keep a backend informed with deltas every few seconds? Right now you’re on your own. You write code to POST fragments to keep your models in sync, or you push whole documents. There’s no mirror/sync capabiity built into the current micro-web-frameworks. The metaphor for the intended save or abandon, feels closer to the commit/merge concept from source control systems.
For enterprise app dev, and to support a following blog entry, I’d like to recap technology choices previously used in the era of the web.
What’s the synopsis? Over the years, logic is progressively migrating from a web-server tier into the browser. Pattens were established in the web-server tier, and were starting to be defined in the web page.

Web applications were still tightly bound to the server hosting them. Java, Python, PHP and Ruby were a few years from becoming realities for web development. Perl perhaps was a possibility for web development, but you were most likely a C++ developer if you were making web-apps. If Awk were viable then, I can’t glean that from the wikipedia page for it. JavaScript was a year away, and wouldn’t be called JavaScript for another two. If you submitted a form and hit the back button, you had little hope of anything useful happening.
Beyond the declarative nature of HTML programming, and the understanding of the contracts behind URLs, there was no functionality in any turing-complete manner present in web pages.
In the year 2000, Sun’s Java Servlets or Java Server Pages (JSP) apps were a possibility with Model-View-Controller being the recommended pattern. Microsoft had recovered from the mistake of ignoring the web in 1995, and had made Active Server Pages (ASP) the most popular enterprise solution. Indeed they had their fork of Java (google for “Project COOL”) .Net ready for enterprise app dev (see later). PHP had come from nowhere as a CGI solution, and Apache with that mechanism of linkage was the most popular web server. You might have even seen /cgi-bin/ in the URLs of web applications, though rewrites were possible. Allaire’s Cold Fusion (an independent IIS data-binding solution) had by then been snubbed when Microsoft didn’t buy the 90% of the company they didn’t already own and launched ASP instead. ASP apps followed the data-binding solution that fatter MS-Access solutions had pioneered for years. ASP.Net was still two years away though. Python and Ruby may have been used in CGI mode, but Rails (in 2003 I foolishly told DHH it wouldn’t scale in the enterprise) was still three years away, and Django two more. Microsoft had pioneered AJAX style functionality in ActiveX controls, and you might have encountered applications that used them. Similarly Sun could still make a claim that Java Applets were a reality on the web at large. Macromedia’s Shockwave/Flash was a viable platform that competed with, and would ultimately beat both ActiveX & Java Applets.
Cookies are in every app by now (thanks to Netscape and Microsoft’s subsequent adoption of them). JavaScript if used, was mostly for simpler validation concepts within pages, before allowing the POST to go through.
AJAX was the big thing. Between them Microsoft, Netscape and Google had made it popular. Most enterprise teams were building apps that were somewhere between Web 1.0 and Web 2.0. Roy Fielding’s REST is big but it required some decoding for the layman. Ruby on Rails had been a significant productivity force for a year or two. The .Net offerings for the web were solid 2.0 a year before and before the end of the year 3.0 would be released. Model-View-Presenter was the paradigm, and Martin talked about it after some initial ThoughtWorks projects with .Net. Microsoft’s patterns and practices group would later talk about it too. That’s not to say that MVC is dead – most of the non-.Net frameworks are coded in accordance with it. All web-app best practitioners were now doing POST-redirect-GET and you finally hit the back button after posting a form – take that Tim Berners-Lee. ThoughtWorks had a year or two before pushed out Selenium for web-testing (and was one of the early pioneers of Comet as a result), then Sahi for another go at the same idea, with WebDriver (now Selenium2) to come a year later. Web applications were now beautifully decomposed solutions, illustrating Separation of Concerns, Inversion of Control, if not Dependency Injection in particular.
JavaScript usage is quite sophisticated now. Applications might issue AJAX operations to the back end to help with adaptive parts of the page, or individual controls. Libraries exist to allow developers to economize and standardize on idioms. These were YUI, Prototype, script.aculo.us, Dojo, and Mootools, but would see future “winner” of the genus JQuery arrive by the years end. Early adopters would try out (in order of revolutionary zeal) ExtJs, and Google Web Toolkit, but the industry remained divided as to whether devs should embrace JavaScript or avoid it completely in the browser. Cappuccino (even more revolutionary) would come a couple of years later, and suffer the same question.
For the purposes of the following article, the take away is that Model-View-Controller in the web-server tier (as delivered by web frameworks) is a big thing in 2006.
破窗户理论大家耳熟能详。身处一个相当糟糕的环境中的时候,如果恶得不到制止,那么恶就会蔓延;如果善得不到赞扬,善也就没有容身之处。这里要说的,却依然是该死的Windows.
Windows 7据媒体声称好了不止一点两点。我也愚蠢的购买了正版。平心而论Windows 7本身还是不错的。然而中文Windows世界长久依赖的混乱,并没有在新版本的到来显得更加和谐一些。各式的应用程序争先恐后的向你炫耀着他们的存在,各式的中文网站则更加火爆的挑战着底线。他们不是流氓软件,他们只是在玻璃渣满地的环境中寻思着下一个能敲的东西。只要是个程序,就恨不得待在右下角状态栏开机就待着,伺机干点什么。
搜狗输入法——一个输入法你开什么新闻窗口啊?还把“不再显示”的那个选择框弄那么小?QQ——安装之后你就不能不推销你的电脑管家?捆绑金山毒霸你也说一声啊?至于迅雷、PPS之类,弹出窗口、换皮肤、常驻右下角,时不时给你弹出个小泡泡说你的机器超过了全国多少多少的机器之类,无法一一而足。
我几乎不用Windows——除了每个月还房贷的时候需要登录网银。每次使用Windows的时候这种愤怒油然而生。可以说这不是Windows的问题,是应用程序的问题——但这种视用户注意力如粪土的破窗户到底从何而生呢?
回到Mac。Mac上几乎不存在打扰你的软件。我正在使用的搜狗输入法Mac版本,没见弹出该死的新闻窗口;偶尔使用的QQ,也没有滴滴滴闹心的声音。甚至我现在正在使用的OmmWriter软件,恨不得把自己藏起来,让你全心的写作。到底是什么,让这些原本鲁莽的流氓斯文起来了?
当衣冠不整进入窗明几净的大厅,人们会自惭形秽;而夹着拖板进入人声鼎沸的集贸市场,对周围的环境自然甘之如饴。Apple(或者说乔布斯)刻意营造出来的这种干净整齐的环境,是一种隐含的暗示:那些糟糕的应用程序,走开吧,这里只接受高质量!
我无数的嘲笑Android手机,及其生态系统。在Android Market中,粗制滥造的程序占据了绝大多数。嵌入的广告也粗鄙不堪。然而Google并不在意。他并不像如同图书馆长一般,建立独一无二的秩序,产生高质量的应用。相比Facebook,人人网显得如同一个乱糟糟的海鲜市场,不合时宜的动画,无处不在的广告,低劣的开发文档,注定了高质量的应用不会出现在这个平台上。
现在已经搞不清楚Windows或者Android秩序的破坏初因何在。但如今平台创建者对敲破玻璃者的姑息引发了更大范围的生态质量的降低。因此,当你创建了一个应用程序,特别是一个某种平台性质的系统,创建只是一小步。要让这个平台成为没有破窗户的完美环境,还有很多的工作。化学中有熵增大原理,生活中,事情也总是在朝混乱方向发展。要想让一件事情自始而终,偏执,近乎信仰的坚持才行。有了乔布斯近乎变态的坚持,才有今天的苹果公司;基于类似的原因,才有了路易斯的《Wired》杂志。
最后突然记起来乔布斯与比尔盖茨唯一的一次电视谈话。最后盖茨由衷的说:我希望拥有乔布斯的品味。而这种品味,是许多人所缺乏的。
From http://www.careerealism.com/the-8-myths-of-delegation/As William Godwin argued more than 200 years ago in his anarchist treatise Political Justice, your team is inevitably the product of its enterprise environment--it took years to get them to be the way they are, and they aren't going to morph into self-governing team members in ten seconds. You can't throw a group of micromanaged developers into a team room and expect them to divide into pairs and start offering to help the testers.I’ve been playing around a little with Goose – a library for extracting the main body of text from web pages – and I thought I’d try converting some of the code to be more scala-esque in style.
The API of the various classes/methods is designed so it’s interoperable with Java code but in order to use functions like map/filter we need the collection to be a Scala one.
That’s achieved by importing ‘scala.collections.JavaConversions._’ which will apply an implicit conversion to convert the Java collection into a Scala one.
I needed to go back to the Java one again which can be achieved with the following code:
import scala.collection.JavaConversions._
val javaCollection = seqAsJavaList(Seq("abc"))
I also used that function in the StopWords.scala object in Goose.
There are a load of other functions available in JavaConversions as well for going to a Dictionary, Map, Set and so on.
千里之行,始于足下 —— 《道德经》
1step是1st step的缩写。
有多少次,一个新想法在脑中成型,我摩拳擦掌,为即将诞生的一批新生代码兴奋不已。不过,真正的第一步,并不是打开编辑器/IDE狂飙代码,而是准备开发环境。
以一个Ruby项目为例,我要做的是:
当然,这还是仅仅是一个个人项目,如果是公司项目,要准备的东西会更多。几乎每一次,要做的事情几乎都是一样的,做多了,自然就烦了。作为一个程序员,我们当然要把自己从这种繁琐中解脱出来。
1step就是一次朝着这个方向努力的尝试。
运行1step很简单,把下边这条命令贴到命令行里。
bash -s stable < <(curl -s https://raw.github.com/dreamhead/1step/master/ruby/starter.sh)
它就会替我们
目前,1step才刚刚起步,我只完成了在Mac环境下Ruby项目的准备。在后续的计划中,我还打算做Rails和node.js环境的安装。
就个人而言,这个项目有几个有趣的地方:
Source: go-gulf.com via Sahana on Pinterest
I’ve been playing around with Turnip: a library that was designed to solve Cucumber’s problems in four main areas:
I don’t really care about having a Cucumber test framework as I don’t often use RSpec, I actually find regexen quite powerful, I also don’t really care about Cucumber’s code base but I can see some merit in having scoped steps.
I converted my existing demo test suite across to use Turnip and this is what I found.
Turnip follows Spinach in trying to move away from using regular expressions to define steps. Fortunately, unlike Spinach, Turnip allows you to capture values using placeholders in your steps (this was an instant NO to using Spinach):
step "I should see at least :exp_num_results results" do |exp_num_results|
on :results do |page|
page.number_search_results.should >= exp_num_results.to_i
end
end
Unfortunately there are a number of limitations to this approach:
You can also do some magic using custom step placeholders, where you define regular expressions, but to me that kinda defeats the point of not using regexen in the first place.
The scoped steps are quite neat and ruby like, and I can see these being very useful for large projects where multiple people are writing the steps.
Running Turnip files is as simple as using the RSpec test runner, but unfortunately the output of pending steps is less useful than Cucumber in that there are no code snippets, and only the first undefined step is displayed as undefined (unlike the Cucumber which shows you every step that is undefined).
Summary
I like the approach that Turnip takes in being a simple way to write business like tests and run them using RSpec. Unfortunately that simplicity comes at a price, and losing the power of regexen in step definitions is not something I would like to give up hastily. I am hoping that the scoped steps someday make their way into the Cucumber code base.
I’m very happy to be a guest speaker at QCon Beijing in April 2012.
I submitted the 2 abstracts below. We haven’t agreed completely on the schedule, but it seems like one of them will be a half-day tutorial (Shallow Depth of Tests) and the other one a talk (Predictably and Irrationally Agile).
Shallow Depth of Tests - Scalable TDD/BDD
Test automation is prevalent in the software development community. Practices like TDD and BDD are widespread and applied almost unquestionably. However, several organisations have struggled in attempting to scale automated test suites, which very often become slow, brittle, non-deterministic, unreliable and difficult to maintain.
In this talk, I will discuss some common mistakes, Tautological TDD (TTDD), for example, and also present patterns that I have successfully applied, such as Shallow Depth of Tests and Testing Pyramid. These have enabled us to achieve maintainable and scalable tests that fulfill their purpose - to help software development teams deliver faster and more confidently the features required by business people.
Predictably and Irrationally Agile
People behavior is one of the centerpieces of Agile software development. Cognitive Psychology and Behavioral Economics have helped us achieve a better understanding of some human seemingly idiosyncratic behaviors, for example, the decoy effect on the decision-making process. Agile teams are constantly making decisions, for instance, while prioritising, estimating stories or choosing the size of an iteration.
This talk will compare and correlate Dan Ariely’s controlled experiments described in his book “Predictably Irrational” to Agile. The anticipated result should be an increased awareness of the reasoning behind some Agile values, principles and practices which, as a consequence, should improve the way we apply them as agile adopters and practitioners.
这几天在澳大利亚出差,何飞问我,在ThoughtWorks做了这么多项目,哪个项目对你的提升最大? 我当时第一反应是Cruise,但我又问我自己,为什么是Cruise? 它带给我什么? 仅仅因为我在这个项目呆够长(呆了两年半)所以对我影响就大?
我仔细回想了我到底从中学到了什么?
尽管这些点都很离散,出现的也很偶然,但它们都有一个共性:
我之所以有收获有沉淀是因为看到了是事情是怎样变好或者变糟的
换句话说,出来混总是要还的,因为项目够长,所以我看到了”还“的那一天,我了解了一个选择可能的后果,以及学会了承受这个选择带来的结果。
新项目,新技术、新客户,一团和气谁不爱? 很多人都最爱新项目,然后做不了两天热情尽了就发脾气打滚要求调动到另一个项目。这样带来的结果是自我感觉太好,没有看到过自己的决策也让焦泥坑变的更黏,自己也并不是一个比任何人高明的程序员。活比较糙,对”魔鬼在细节中“没有感觉,因为不了解为什么要把log写好,error message写好,从不承受选择的后果也让人心智不能很快的成熟。
此外,往往是大而复杂的项目才能够催生出真正的创新,做Cruise的时候因为build的时间太长(2小时),一天两天也就忍了,当面对的是一个永无尽头的项目,我们就必须鼓起勇气想办法把问题解决了,忍无可忍之下,用了几个周末,我和教授、Derek一起作出了test load balancer把build的时间缩短到20分钟。
从这个角度看,PwC, REA以及未来的Sun记正是最好的舞台。
Show options
Hide options
Additional options
Avoid highways
Avoid tolls
KM
Miles
Get Directions
Print Directions
jQuery(document).ready(function() {
var map_0cc460a49c49903bbc69a80b521007fb = new google.maps.Map(document.getElementById("0cc460a49c49903bbc69a80b521007fb"));
var orc = new jQuery.GoogleMapOrchestrator(map_0cc460a49c49903bbc69a80b521007fb, {bubbleAutoPan: "", zoom : 12, mapType: google.maps.MapTypeId.SATELLITE});
orcHolder.push({mapId: "0cc460a49c49903bbc69a80b521007fb", orchestrator: orc});
orc.switchMapControl(true, jQuery.GoogleMapOrchestrator.ControlType.MAPTYPE);
orc.switchMapControl(true, jQuery.GoogleMapOrchestrator.ControlType.PAN);
orc.switchMapControl(true, jQuery.GoogleMapOrchestrator.ControlType.ZOOM);
orc.switchMapControl(true, jQuery.GoogleMapOrchestrator.ControlType.SCALE);
orc.switchMapControl(true, jQuery.GoogleMapOrchestrator.ControlType.STREETVIEW);
orc.buildAddressMarkers("178 Hawthorn Road, Caulfield, Melbourne, Australia{}cloudy.png");
});
More from Alexander Zagniotov:
ThoughtWorks embraces the individuality of the people in the organization and hence the opinions expressed in the blogs may contradict each other and also may not represent the opinions of ThoughtWorks.