├── readme-zh.md └── readme.md /readme-zh.md: -------------------------------------------------------------------------------- 1 | # 服务设计的原则 2 | 3 | - [服务设计的原则](#service-principles) 4 | - [创建](#creation) 5 | - [首先探讨系统的整体架构](#discuss-the-organization-of-your-overall-system-first) 6 | - [核实是否能够给现有服务添加新的功能](#check-whether-you-can-add-your-feature-to-an-existing-service) 7 | - [考虑你的功能是否更适合作为一个库](#consider-whether-your-feature-is-better-suited-to-a-library) 8 | - [服务应由团队而不是个人负责管理](#services-are-curated-by-teams-not-individuals) 9 | - [服务是长期的commitment](#services-are-a-long-term-commitment) 10 | - [部署到分布式系统之前的考虑因素](#factor-in-the-overhead-of-deploying-a-distributed-system) 11 | - [服务大点更好](#prefer-larger-services) 12 | - [最小化服务调用链的深度](#minimize-the-depth-of-the-service-call-graph) 13 | - [最小化你的团队所拥有的服务数目](#minimize-the-number-of-services-owned-by-your-team) 14 | - [接口](#interfaces) 15 | - [接口要易于理解](#interfaces-should-be-easy-to-understand) 16 | - [接口要尽可能健壮](#interfaces-should-be-robust) 17 | - [接口变更应该要向后兼容](#changes-to-interfaces-should-be-backwards-compatible) 18 | - [测试](#testing) 19 | - [应该能够对任何接口变更进行自动化测试](#any-changes-to-your-service-should-be-able-to-be-tested-automatically) 20 | - [最主要的是测试你的接口](#your-interface-is-the-most-important-thing-to-test) 21 | - [运维](#operations) 22 | - [服务的运行由你来负责](#you-are-responsible-for-running-your-service) 23 | - [引导客户的期望值](#guide-your-clients’-expectations) 24 | - [故障计划](#plan-for-failure) 25 | - [补充材料](#additional-reading) 26 | 27 | 28 | ## 创建 29 | 30 | ### 首先探讨系统的整体架构 31 | 32 | 在你开始考虑设计服务之初,也就是动手写代码和设计之前,和团队成员、其他服务领域的专家聊一聊。除了如何与现有的特性、产品以及服务如何适配之外,考虑一下你想要额外添加的功能。考虑一种最合理的组织整体功能的方式。有时候添加新功能意味着要对现有组件进行重组。 33 | 34 | 35 | 我们希望避免那些简单的 "append-only"服务架构,也就是说development只存在于新的服务中 36 | 37 | ### 核实是否能够给现有服务添加新的功能 38 | 39 | 在编写新的服务之前,应该核实是否现有服务不包括你的功能。它可能会与现有的功能存在冲突,处理相同的信息,或者只是在现有服务功能范围内的演化。另一方面,如果给现有服务添加新的功能会让服务的使用者感到困惑的话,最好就不要添加新的功能了。 40 | 41 | ### 考虑你的功能是否更适合作为一个库 42 | 43 | 尽管这篇文档是讲服务的,但值得注意的是一些功能更适合做成库。为了帮助大家更好的决策,我们介绍了库与服务之间进行取舍的一些经验: 44 | 45 | ***升级速度*** 对于库来讲更适合哪些用户期望很长时间才进行升级的场合(通常数周或数月,甚至于数年)。一般来说,这要求功能本身相对小且自包含,所以本身变更的可能就小。相反,如果你还正在进行开发,或者你希望能够快速推送一些bug修订给用户,这样的功能更适合作为一个服务。这样功能就会更复杂一些,常常需要依赖外部的一些库。 46 | 47 | ***性能和可靠性*** 库与调用请求的寻址空间一样,而服务则处于不同的网络地址。假使其他东西都是一样的,访问一个库 要比服务更快更可靠。但是,如果功能对资源需求较高,比如CPU,内存和硬盘,那么作为一个服务能够让客户端的运行效率更高,能够使得服务所依赖的硬件独立于客户端的硬件而水平扩展。 48 | 49 | ***技术无关性*** 50 | 大多数情况下,Yelp使用的是Python,有一些后台服务是Java的。如果你的服务希望 Python and Java都能调用,那么做成服务能够帮你避免重复写两个库。 51 | 52 | ### 服务应由团队而不是个人负责管理 53 | 54 | 每个服务应由团队而不是个人负责管理,这样出了问题,就不会出现只有一个开发人员知道如何解决bug。实践中也就是说,每个服务从最开始都至少要2个人员参与,分摊维护的责任。 55 | 56 | ### 服务是长期的投入 57 | 58 | 当你编写代码时,你的团队会进行持续的维护和运营支持。随着时间推移,你的服务用户将依赖服务才能够运营。你需要持续的监控性能、一致性和 brown-outs.你或许也要处理下游系统的故障。 59 | 60 | ### 部署到分布式系统之前的考虑因素 61 | 62 | 一个服务的推出往往花费的时间比你想象的要长。这可能是由于未预料到的不同服务间的交互,或者是搭建监控环境时的困难所导致的。负载测试没有100%完美的,因此需要部署成功之前制定一个多个阶段和迭代的持续计划。 63 | 64 | ### 服务大点更好Prefer larger services 65 | 66 | 尽量把相关功能整成一个较大的服务而非多个很小的服务。记住 你的大服务应该是逻辑内聚的,比如你仍然要具体的描述服务的功能。该原则的原理如下: 67 | 68 | * 流程内调用比服务间调用要更快、更可靠 69 | * 单独更改一个服务比在多个服务间协调变更要简单一些。实际上,更改服务接口的成本相对是很高的 70 | * 在运营层面上,保证统一的流程正常运行的话,数量越少越容易. 71 | * 有些运维任务必须在每个服务上单独进行,比如库版本的更新。服务更少这个工作量越小 72 | 73 | ### 最小化服务调用链的深度 74 | 75 | 在对服务进行架构时,调用链长度越小越好 (service ``A`` calls services ``B``, ``C`` and ``D``)比(``A`` calls ``B`` calls ``C`` calls ``D``)好。原理如下: 76 | 77 | * 更容易来推出调用链; 在长度较小的情况下, B, C and D 没有依赖任何外部的服务,而在较长的情况下B和C服务都依赖其他服务。注意 这与 [three-tier architecture](http://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture)很匹配。 78 | * 这样能提高性能,在较短的情况下,服务的调用可以并发执行(只要服务间互相独立即可)而较长的情况下只能串行执行 79 | 80 | 81 | ### 最小化你的团队所拥有的服务数目 82 | 83 | 你所在的团队可能负责某个产品或一些产品的交付。你们所开发的服务应该与这一目标相统一。如果你们所拥有的服务过多,也就是说你的团队过于面面俱到,或者你们服务的统一程度还不够. 84 | 85 | 如果你们团队已经在负责大量的重要服务,且新功能并不属于任何已有的服务,在你们团队能够接受构建新服务的任务之前你可能需要把任务推出去。也应避免2个以上团队负责某个服务的情况 86 | 87 | 88 | ## 接口 89 | 90 | 你的服务接口可能不只是对外发布的[REST](http://en.wikipedia.org/wiki/Representational_state_transfer) 接口. 日志、数据备份、数据流等其他服务使用的都要考虑在内。接口指的是对客户端有用的所有东西的总和. 91 | 92 | > 比如我们一直在模仿的接口设计的实践, 参考 [GitHub API v3](https://developer.github.com/v3/) 和 [PayPal REST API](https://developer.paypal.com/docs/api/) 93 | 94 | ### 接口要易于理解 95 | 96 | 在设计接口时, 要遵循如下最佳实践: 97 | 98 | * 使用自描述的名称。对内对外保持一直 99 | * 让领域专家评审你的接口 100 | * 使用一种显而易见的方式来完成每个操作 101 | * 在将现有功能移植成服务时不一定会成为最好的网络节点。远端执行会改变一致性、可靠性和性能的本质。设计服务的边界时应保证与其他系统是松耦合的. 102 | * 将读与更新操作的接口分离. (See [CQRS](http://martinfowler.com/bliki/CQRS.html) as an example) 103 | * 尽可能的最小化、简单化你的接口。这样测试起来也方便 104 | * 尽可能对存在疑虑的地方写上文档 105 | * 跟自己发问: "在没有和你请教之前,新人能够看懂你的接口?" 106 | 107 | ### 接口要尽可能健壮 108 | 109 | 设计接口时把它当做是暴露给整个互联网来使用的。只暴露那些客户端需要的信息。尽可能不要提供不安全的、高成本的操作 110 | 111 | 将只读和能够改变状态的方法区分开来。理想情况下,只读方法是可缓存的且是幂等的 112 | 113 | 114 | ### 接口变更应该要向后兼容 115 | 116 | 接口应该有某种版本控制机制。当改变一个服务接口时,之前存在的客户端仍然能够调用你之前版本的接口。你不能破坏这些客户端。要么做好计划持续的支持这些旧的客户端,要么花些时间使得它们和更新相协调。同时有多个版本的接口并存是可以接受的。 117 | 118 | 119 | ## 测试 120 | 121 | ### 应该能够对任何接口变更进行自动化测试 122 | 123 | Yelp没有单独的QA工程师。相反我们靠计算机来进行校验工作。你的服务由你来负责维护测试工具。测试应该在开发和测试环境中都能够快速可靠的完成。 124 | 125 | 一个优秀的测试工具就像一个金字塔。最基础的是单元测试:很多快速的很小的测试代码中的单个实现的细节。中间一层是集成测试,这时候对服务进行交互的多个组件进行校验。最上面是很小但很重要的验收测试,主要校验你的服务是否能够与其他服务进行交互 126 | 127 | ### 最主要的是测试你的接口 128 | 129 | 接口是你们协议的重要部分,也就是客户端使用和交互的东西。如果你改变了接口,破坏或改变它原来的功能你会影响所有调用你服务的客户端。这个影响是很大的。 130 | 131 | 这也就是为什么接口测试是最主要的。接口测试能够告诉你客户端实际看到的是什么,持续的测试能够保证客户端总是看到这些结果。确保接口的所有现用版本的性能是一致的。尽可能早的编写测试,整个黑盒测试所看到的结果应该驱动你的接口设计 132 | 133 | ## 运维 134 | 135 | ### 服务的运行由你来负责 136 | 137 | 你的团队负责你们服务的持续运维。运维团队是解决整体站点问题的第一道防线,但是涉及到服务就只能做些边边角角的工作。监控服务的健康度是你的职责所在,做一些有意义的提醒以及出现问题时的方案。你是最了解服务运行机制的人,因此你也是发现和解决问题的最佳人选 138 | 139 | 为服务编写runbook。其中应包含常见的运维场景(诸如部署、监控和问题处理)。记录已知的错误。 140 | 141 | ### 引导客户的诉求 142 | 你要能够准确清晰的向客户传达你们服务的运行特征。我们建议你要积极地监控和对服务进行测试以理解这些特征,以确保哪些你所承诺要达到的维护程度。 143 | 144 | 比如,如果我告诉客户99.99%的正常运行情况下99%的请求都在100ms以下。也就是说我要不停的监控性能和可用性来保证我的承诺。 145 | 146 | 不同服务运维承诺不同是可取的。a "cat picture of the day"服务的正常运行时间只是团队内部用来调剂气氛的就不需要实时的监控和提醒了。一个生产系统中的对主要节点很重要的服务就需要多关注正常运行时间、性能和其他可能会出现的问题 147 | 148 | 149 | ### 故障计划 150 | 151 | SOA的特质之一是分布式系统中故障场景数量的急剧增加。从运维角度来看,这意味着你的服务必须 152 | strive to be honest about the collaborating services and datastores it requires to operate, and those whose downtime can be gracefully handled. 153 | 154 | 服务启动之初就要有如何确定外部的依赖关系和记录它们之间关系的计划。外部链接的指标,以及主动式和开发层面的保护措施以保障服务在外部故障之后的处理。最后一点,你要实时的监控这些潜在的weak points,根据严重程度来提醒当班团队. 155 | 156 | 另外,服务可能在跨机器、跨rack和跨数据中心间运行,每种情况都可能出现故障。要深刻理解这样的场景下服务的表型。 157 | 158 | 设计良好的服务要考虑所有的故障点,做出合理的决策来减少每个故障的可能性。 159 | 160 | 161 | ## 补充材料 162 | 163 | * [Martin Fowler on Microservices](http://martinfowler.com/articles/microservices.html) 164 | * [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter) 165 | * [Steve Yegge's Google Platforms Rant](http://steverant.pen.io/) 166 | * [The Fallacies of Distributed Computing](http://en.wikipedia.org/wiki/Fallacies_of_distributed_computing) 167 | * [Designing Poetic APIs](http://pyvideo.org/video/2647/designing-poetic-apis) 168 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Service Principles 2 | 3 | - [Service Principles](#service-principles) 4 | - [Creation](#creation) 5 | - [Discuss the organization of your overall system first](#discuss-the-organization-of-your-overall-system-first) 6 | - [Check whether you can add your feature to an existing service](#check-whether-you-can-add-your-feature-to-an-existing-service) 7 | - [Consider whether your feature is better suited to a library](#consider-whether-your-feature-is-better-suited-to-a-library) 8 | - [Services are curated by teams, not individuals](#services-are-curated-by-teams-not-individuals) 9 | - [Services are a long-term commitment](#services-are-a-long-term-commitment) 10 | - [Factor in the overhead of deploying a distributed system](#factor-in-the-overhead-of-deploying-a-distributed-system) 11 | - [Prefer larger services](#prefer-larger-services) 12 | - [Minimize the depth of the service call-graph](#minimize-the-depth-of-the-service-call-graph) 13 | - [Minimize the number of services owned by your team](#minimize-the-number-of-services-owned-by-your-team) 14 | - [Interfaces](#interfaces) 15 | - [Interfaces should be easy to understand](#interfaces-should-be-easy-to-understand) 16 | - [Interfaces should be robust](#interfaces-should-be-robust) 17 | - [Changes to interfaces should be backwards compatible](#changes-to-interfaces-should-be-backwards-compatible) 18 | - [Testing](#testing) 19 | - [Any changes to your service should be able to be tested automatically](#any-changes-to-your-service-should-be-able-to-be-tested-automatically) 20 | - [Your interface is the most important thing to test](#your-interface-is-the-most-important-thing-to-test) 21 | - [Operations](#operations) 22 | - [You are responsible for running your service](#you-are-responsible-for-running-your-service) 23 | - [Guide your clients’ expectations](#guide-your-clients’-expectations) 24 | - [Plan for failure](#plan-for-failure) 25 | - [Additional Reading](#additional-reading) 26 | 27 | ## Creation 28 | 29 | ### Discuss the organization of your overall system first 30 | 31 | As you start thinking about designing your service, talk with your teammates and other service experts before writing any code or even design documents. Think about the features you want to add in addition to how they fit in with any existing features, products, and services. Think about the most sensible way to organize the entire feature set. Sometimes adding new features warrants a reorganization of existing components. 32 | 33 | We want to avoid "append-only" service architecture where development only happens in the form of new services. 34 | 35 | ### Check whether you can add your feature to an existing service 36 | 37 | Before writing a new service, you should check that your feature doesn’t belong in any existing service. It may overlap with existing functionality, deal with the same information, or be a natural progression of an existing service’s scope. On the other hand, if adding the new functionality to an existing service would be surprising and confusing to users of that service, it probably doesn't belong there. 38 | 39 | ### Consider whether your feature is better suited to a library 40 | 41 | Even though this is a document about services, it's important to point out that some features are better suited to libraries. To help you make the right call for your feature, we describe some of the tradeoffs between libraries and services: 42 | 43 | ***Upgrade speed*** Libraries are best suited to situations where it's acceptable for users to upgrade over long periods of time (normally weeks to months, sometimes years). Typically, this requires that your feature is relatively small and self-contained, resulting in a low rate of change. In contrast, if you anticipate lots of ongoing development, or you may need to quickly roll out a bugfix to all users, then your feature may be better suited to a service. This will tend to occur with more complicated features, potentially with external dependencies. 44 | 45 | ***Performance and reliability*** Libraries live in the same address space as the calling process, whereas services live on the other end of a network cable. All other things being equal, it's going to be faster and more reliable to access your feature as a library. However, if your feature has high resource requirements (e.g. CPU, memory or disk) then implementing it as a service can allow clients to run more efficiently and allow the service hardware to be scaled independently of the client hardware. 46 | 47 | ***Technology independence*** For the most part, Yelp is a Python shop, with a few Java systems on the backend. If your feature does need to be accessed by both Python and Java clients, then writing it as a service is going to allow you to avoid writing two different library implementations (one for each of Python and Java). 48 | 49 | ### Services are curated by teams, not individuals 50 | 51 | Each service should be curated by a team instead of an individual. This prevents situations where only one developer knows how to fix the service. In practice, this means that every service should be developed by more than one person from the very beginning, and operational responsibilities must be shared. 52 | 53 | ### Services are a long-term commitment 54 | 55 | When you write any code, you are committing your team to providing ongoing maintenance and operational support. Over time you will build up a set of consumers of your service that will depend on it to stay operational. You need on-going monitoring for performance, consistency, and brown-outs. You may also need to respond to failures in downstream systems. 56 | 57 | ### Factor in the overhead of deploying a distributed system 58 | 59 | The initial rollout of a service often takes longer than you might think. Sometimes a lot longer. This could be due to unexpected interactions between other services or difficulties in setting up the right monitoring. Load testing is rarely perfect so plan on going through several stages and iterations before being 100% deployed. 60 | 61 | ### Prefer larger services 62 | 63 | Aim to place related functionality into a single, larger service instead of multiple, smaller services. Note that your larger service should be logically cohesive i.e. you should still be able to concisely describe its behavior. The rationale for this guideline is as follows: 64 | 65 | * In-process calls are more reliable and faster than inter-service calls. 66 | * It is often easier to change just a single service instead of coordinating changes across multiple services. In particular, it is relatively expensive to change service interfaces. 67 | * At the operations level, it tends to be easier to keep a smaller number of more uniform processes correctly running. 68 | * There are some maintenance tasks which must be separately performed on each service e.g. upgrading to new versions of libraries. It is easier to do this if there are fewer services. 69 | 70 | ### Minimize the depth of the service call-graph 71 | 72 | When architecting services, prefer a shallow inter-service call-graph (service ``A`` calls services ``B``, ``C`` and ``D``) to a deep one (``A`` calls ``B`` calls ``C`` calls ``D``). The rationale for this principle is: 73 | 74 | * It is often easier to reason about shallow call-graphs; in the shallow case, B, C and D have no external service dependencies, whereas in the deep case both B and C depend on another service. Note that this fits naturally with the [three-tier architecture](http://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture). 75 | * It tends to increase performance; in the shallow case, the service calls can be executed in parallel (as long as they are independent), whereas in the deep case all code runs serially. 76 | 77 | ### Minimize the number of services owned by your team 78 | 79 | Your team is [hopefully] organized to deliver some product or related set of products. The services you develop should be aligned with that focus. If you have too many services, it means either your team is spread too thinly across dissimilar systems, or you're not unifying your services enough. 80 | 81 | If your team is already responsible for a large number of critical services (ex: more services than developers), and the new feature does not fit into any of the existing services, you may need to re-distribute responsibility before your team is able to take on the task of building another service. You should also avoid sharing responsibility for services across more than one team. 82 | 83 | ## Interfaces 84 | 85 | The interface to your service may be more than just its public [REST](http://en.wikipedia.org/wiki/Representational_state_transfer) interface. Logs, data dumps and streams that are consumed by other services should be considered. Your interface is the sum of all the parts that are usable by clients. 86 | 87 | > For examples of good interface design we try to follow, see the [GitHub API v3](https://developer.github.com/v3/) and the [PayPal REST API](https://developer.paypal.com/docs/api/) 88 | 89 | ### Interfaces should be easy to understand 90 | 91 | When designing an interface, follow best-practices such as: 92 | 93 | * Use self-describing names. Be consistent internally and externally. 94 | * Have domain experts review your interface. 95 | * Have one obvious way of performing each operation. 96 | * When porting to a service your existing functions won’t necessarily make the best network endpoints. Remote execution changes the nature of consistency, reliability, and performance. Design your service boundary to be loosely coupled with other systems. 97 | * Separate the interfaces that read from those that update. (See [CQRS](http://martinfowler.com/bliki/CQRS.html) as an example) 98 | * Minimise and simplify the interface as much as possible. This will also aid in testing. 99 | * Document any areas where confusion may arise. 100 | * Ask yourself: "Could a new-hire understand your interface without having to talk to you?" 101 | 102 | ### Interfaces should be robust 103 | 104 | Design your interface as if it is going to be exposed to the wider Internet. Only expose information [that is strictly required by clients](http://en.wikipedia.org/wiki/Law_of_Demeter). Where possible, avoid providing unsafe or expensive operations. 105 | 106 | Differentiate between read-only methods vs those that change state. Ideally your read-only methods are idempotent and cacheable. 107 | 108 | ### Changes to interfaces should be backwards compatible 109 | 110 | Your interfaces should have some versioning mechanism. When you change a service interface, pre-existing clients will still be invoking the previous version of your interface. You must not break those clients. Plan on continuing to support these older clients, or to be spending additional time coordinating with them about upgrades. It’s acceptable to have several versions of an interface in use at the same time. 111 | 112 | ## Testing 113 | 114 | ### Any changes to your service should be able to be tested automatically 115 | 116 | Yelp doesn't employ separate QA engineers. Instead, we rely on computers to do our validation. You are responsible for maintaining a good test suite for your service. Your tests should run quickly and reliably in both dev and test environments. 117 | 118 | A good test suite is like a pyramid. At the base are your unit tests: many fast, small tests that validate individual implementation details of your code. In the middle are your integration tests, in which you validate how various components of your service interact. At the top is a small but critical set of acceptance tests that validate your service works correctly with dependent services. 119 | 120 | ### Your interface is the most important thing to test 121 | 122 | Your interface is an important part of your contract. The interface is what your clients use and interact with. If you change your interface and break or change how it behaves you're affecting all clients of your service. This can have widespread impact. 123 | 124 | This is why your interface is the most vital component to test. Your interface tests will tell you what your client actually sees, while your remaining tests will inform you on how to ensure your clients see those results. Ensure that all active versions of your interface perform consistently. Write these tests as early as possible, since the desired behavior from a blackbox testing perspective should be driving your interface design. 125 | 126 | ## Operations 127 | 128 | ### You are responsible for running your service 129 | 130 | Your team must be committed to the on-going maintenance of your service. Your friendly Operations team is the first line of defense when there are overall site problems, but they're mostly doing triage when services are involved. It's your responsibility to monitor the health of your service, have meaningful alerts, and have a plan of action in the event of problems. You are the one who knows best how your service works, so you're best positioned to identify and fix problems when they arise. 131 | 132 | Write a runbook for your service. Fill it with common operational cases (such as deployment, monitoring, and troubleshooting). Document known errors. 133 | 134 | ### Guide your clients’ expectations 135 | 136 | You need to be able to clearly communicate to clients the operational characteristics of your service. We suggest that you actively monitor and performance test your service in order to understand these characteristics and that you commit to certain thresholds that the service should maintain. 137 | 138 | For example, if I tell my clients that "99% of requests return in under 100ms with 99.99% uptime", it means I should be actively monitoring the performance and availability of my service to ensure that I'm sticking to my guarantee. 139 | 140 | It's okay for different services to have different operational guarantees. The uptime of a "cat picture of the day" service that's only used internally by your team for laughs may not even be actively monitored or alerted. A service that's production-facing and part of the critical path to major endpoints should have a much higher commitment to uptime, performance, and any problems that arise. 141 | 142 | ### Plan for failure 143 | 144 | One of the major defining features of a Service Oriented Architecture is a vast increase in the number of possible failure scenarios in your distributed system. Operationally, this means your service must strive to be honest about the collaborating services and datastores it requires to operate, and those whose downtime can be gracefully handled. 145 | 146 | Plan from the start of your service to identify external dependencies and document your relationship to them. Place metrics on external links, and put in place both passive and developer-operated protections so you can protect your service against external failures. Finally, make sure you actively monitor these potential weak points, and alert your on-call team appropriately depending on their severity. 147 | 148 | In addition, your service will likely be running across multiple machines, racks, and datacenters, each with their own possibility of failure. Understand how your service will behave under each scenario. 149 | 150 | Well-designed services consider all and every failure points and make a conscious decision on how to degrade in every single one of these. 151 | 152 | ## Additional Reading 153 | 154 | * [Martin Fowler on Microservices](http://martinfowler.com/articles/microservices.html) 155 | * [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter) 156 | * [Steve Yegge's Google Platforms Rant](http://steverant.pen.io/) 157 | * [The Fallacies of Distributed Computing](http://en.wikipedia.org/wiki/Fallacies_of_distributed_computing) 158 | * [Designing Poetic APIs](http://pyvideo.org/video/2647/designing-poetic-apis) 159 | --------------------------------------------------------------------------------