2013年12月6日 星期五

筆記:Domain driven design quickly


什麼是DDD

參與角色:Domain Expert,Analyst,Developer
產品:Domain Model,Software Design

Domain:一個會實際存在,被使用的系統。不同的系統有不同的運作邏輯、流程、資料。也是軟體要幫助的對象。Ex:銀行、書籍管理、進銷貨、股票交易,電信,這些是原來就有系統存在,軟體可以幫助這些系統運行。而像是遊戲、作業系統、這些原來沒有存在的系統,軟體就是把系統行為建構起來的基本組件。

Model:把實際存在,運作的事物抽象化就是Model,Ex:經濟模型、數學模型、物理模型...等,Model不是一個特定的圖,Model是一個idea,Model不只是Domain專家的知識,Model是一個嚴格組織與挑選過的,對於Domain專家的知識的抽象化。Model可以用圖來表示,可以用code來表示,可以用文字來描述。對於開發人員而言,Model就代表Domain。
  • Software Develop花太多的時間在code上,他們認為軟體就只是Object與Method。
  • 建立Model的最主要目的,就是要拿來溝通。
  • Software Design可以說是整間房子的藍圖,Code Design算是實際蓋房子的方法。
  • Design Pattern就是Code design的一種方法
  • Waterfall的缺點就是作到最後沒人敢動
  • Agile的方式的缺點就是一開始沒有作周詳的設計
  • To be or not to be?
  • Domain專家講的一定都對?No,Domain專家的語言不見得可以直接拿來做軟體,所以要問清楚,再作Modeling,Model也會不斷做修改。
  • DDD的目標就是透過與Domain專家溝通,作對應Domain的Mode,再根據Model,來作出Software Design,讓Design可以與Domain對應,而且讓參與製作產品的所有人,都可以透過Model這個共通的語言來溝通

The Ubiquitous Language
  • Software developers腦袋裡面裝的就是Classes,Methods,演算法,Patterns,而且有一種要把現實世界所遇到的問題配合這些觀念的傾向,所以作出來的Design只有搞軟體的才會懂。這就是我們一直以來作事的方法。
  • 所以與Domain專家討論時,Software developer傾向於講libraries,frameworks,Persistence,Database,這些純技術名詞,Domain專家無法了解。
  • 作出符合需求的軟體->與Domain專家與其他人員更好的溝通->The Ubiquitous Language
  • The Ubiquitous Language是由雙方討論出來的,比較偏Domain expert的行話。
  • UML可以表達關係,一個一個的個體,但是沒辦法詳細表達每一個個體的意義,或是每一個個體的功能。
  • 用UML圖表現個體,文字詳述每個個體代表的概念,限制,或是互動。一次描述Domain的一個部份。這似乎可以用在Function Spec裡面。
  • 畫出代表整個系統的單一UML意義不大,不如根據不同概念畫出不同部分的UML。
  • Code不能完全代表Model,不可能給Domain Expert看,因為裡面太多實作細節,例如用了Design Pattern,特殊演算法...等

Model-Driven Design
  • Domain專家,系統分析師,Deveoper最好可以一起Design
  • 最後可以把Model作成可以用code表達的方式,如果設計一個無法實作的Mode,再怎麼完全貼近Domain也沒用。
  • 設計model也要能符合軟體架構,軟體才有機會按照model作出來。
  • OOP比較適合用來實作Model
  • 函式導向比較不適合,但是可以作不是拿來給人使用,而是內部自行運作控制的系統,或是科學運算,Ex:OS,嵌入式系統

Entities,Value Objects,Services,Repositories,Factories,Aggregates,Layered Architecture,每一個都是一個Pattern,Model-Driven-Design就是作出一個Model,可以用這些Pattern實作的設計方式。

Layered Architecture
作業系統有分層,網路堆疊有分層,連生物的構造都有分層,為什麼軟體不該分層?我自己寫有分功能就不錯了,分層就距離很遠。在Layered Architecture中,只有上層能呼叫下層。同層之間的Module可以相互呼叫。
  • User Interface:負責與使用者互動,GUI,CLI
  • Application:薄薄的一層,用來協調Domain層不同原件的互動,此層不包含Business logic,也不會存任何Business data,可以存放user request的進度。等同MVC的C,表現User Stories的地方。
  • Domain:表達Model的一層,也是軟體最有價值的地方,放Business logic,Business data。
  • Infrastructure:底層系統功能,資料庫,系統功能,繪圖,網路。
Entities
代表在Model中的一個資料個體,在程式執行中具有唯一性,有自己的狀態,有自己的行為,應該要在Model開始建立的時候就要設計出來。Ex:Account in banking system,每個Account有ID,每個Accout之間就算資料相同,還是不一樣的Account。

Value Objects
代表在Model中不需唯一性的資料個體,理論上,應該把不需唯一性的資料個體全部定義為Value Object,最好是能設計成Immutable,可以大大減少系統複雜度與執行效率。有一個規則是,如果Value Objects會被不同Class間分享,那就應該定義為Immutable。Ex: Customer包含Address,Adress包含Street,City,State。Customer是一個Entity,Address沒有唯一性,所以是Value Object。

Services
不屬於任何Entities與Value Objects的行為,或是跨越好幾個Entities與Value Objects的行為,把多個類似的行為集合,定義為Service,Service不能有內部狀態,只有提供方法讓外界呼叫執行,而後得到結果。好處就是可以降低Object之間的耦兒,而且把行為的概念萃取出來,Service有以下三個特徵:
  1. 在Service裡面的操作不屬於任何Entity或是Value Object
  2. 在Service裡面的操作參考到Domain裡其他Objects
  3. 在Service裡面的操作沒有狀態
Ex:Banking system裡面的轉帳,從A帳戶轉到B帳戶,就可以是一個Service。而轉帳狀態也會有一個Entity來負責記錄。

Application Layer可以有Service,Domain Layer可以有Service,Infrastructure Layer也可以有Service,在建立Model是要把所屬的Layer分清楚。

Service的名稱要是The Ubiquitous Language,大家看得懂。

Modules
Layer是縱向,Modules則是橫向,包含一群相關的Entities,Value Objects,Services,對外提供定義好的接口供其他Module呼叫。有兩種聚合Module的方式:
  1. communicational cohesion:都在傳同類的資料
  2. functional cohesion:一起完成一系列相關的動作
Module也應該作Refactor,搬來搬去
Module的名稱也要是The Ubiquitous Language,大家看得懂。

Aggregates
一群相關的Enities與Value Objects集合在一起成為一個聚合體,對外提供服務,從外面看到的只有這個Aggregates,外界可以使用的也只有這個Aggregate,而Aggregate本身也是一個Entity,因為聚合其他Objects,所以是Root。當Aggregate裡面的某一個Objects改變的時候,Aggregate內相關的資料也會一起改變。而Aggregate外面的Object不會有任何變化。
  • Aggregate可以包含Aggregate。所以可以有各式各樣的包含變化。
  • Aggregate包含一群Objects。這些Objects資料互相關連,Aggregate負責保證這些Object的資料合理正確。
  • 這一群Objects可以互相參考,但是不能參考外界的Objects。把複雜度局限在Aggregate裡。
  • 外界只能參考Aggregate,不能直接參考裡面的Objects。外界不能破壞Aggregate內部資料運行的邏輯。
  • 外界要改變Aggregate包含的Object內容,只能透過呼叫Aggregate的方法。所以外界修改都在Aggregate的保護之下。
  • Aggregate把object丟出去只能丟copied value object。
由以上幾點,可以達成資料完整性與不變條件。Aggregates可以保證修改與刪除都不會出包,而新增就由Factory處理。
Ex:Customer是一個Aggregate,包含Address,ContactInfo。呼叫Customer.getAddress()會回傳一個Clone出來的Address,是一個Value Object。使用者修改成無效的Address,也不會影響Customer。當要修改Customer內的Address的時候,要呼叫Customer.setAddress(),內部會檢查Address是否有效。有效的話就Clone一份放到Customer裡面。

Factories
用來封裝Objects建立的流程與邏輯,所以建立Objects的知識,就是Domain的知識,全部都應該放在Factories裡面。因此透過Factories建立出來的Objects都是可用的。

為什麼要有Factorys?當Entities與Aggregates太大太複雜,使用者想要產生一個Entities或Aggregates時,必須要有足夠的知識才能產生,大大的增加使用的難度,而且產生出來的Entities或Aggregates,內容可能有問題,使用者還要想辦法把它設定正確,才能夠順利儲存。就算使用者將Entities或Aggregates設定正確了,這些使用的方式,邏輯,都會寫在外部,讓外部的邏輯更加複雜,並且破壞了封裝,還造成沒有重用性以及重複的程式碼。

Aggregate,Value Objects,Entities都可以用Factories來產生。

可以使用的方法如下:
  • Factory Method->From Design Pattern
  • Abstract Factory->From Design Pattern
  • Constructor->只在Create Objects很簡單的狀況下使用
Ex:生成一個User,ID必須用查表的方式獲得,而且系統會有最大User數量限制,User是一個Interface,有三個實作分別是NormalUser,VIPUser,LimitedUser。每一種實作分別有不同的預設值。如果用Factories,則會是
  • UserFactory.Create(UserType.NormalUser);
  • UserFactory.Create(UserType.VIPUser);
  • UserFactory.Create(UserType.LimitedUser);
所有需要創建User的知識就放在UserFactory裡面,外部使用者只需呼叫UserFactory.Create就可以得到一個可用的User。

Repositories
包裝所有儲存,取出Objects的邏輯,提供儲存Objects的地方,也就是Aggregates,Entities,Value Objects都可以儲存,使用者不需知道底層的儲存方式為何,可以是記憶體,可以是檔案,可以是SQL資料庫,可以是NoSQL資料庫,也可以是OOO。
  • 因為隨時都可以透過Repository拿Objects,使用者(包含Domain內部),可以儘量不持有Objects的References。有需要再透過Repositories來拿。
  • 提供單一的查詢機制,因此使用者也不需在意底層儲存媒介提供的查詢機制,當然也可以作快取。
  • 因為與儲存媒介溝通,看起來很像處在Infrastructure Layer,其實不然,Repositories是在Domain Layer,也需提供Application Layer服務。
  • Repositories與Factories有何不同?Factories負責Create Objects,而Repositories負責照顧已經存在的Objects,所謂已經存在,可能在記憶體,也可能在其他儲存媒介,只要經過Repository.save之後就算存在。
  • Factories算是純Domain,而Repositories包含與Infrastructure的連結。


Refactoring Toward Deeper Insight
  • 設計需要,洞察力、經驗、深思、才能。那我看這本幹嘛
  • Domain model不是一次就可以做好,也需要透過迭代的方式來慢慢培育
  • 所以Design不是作完就不動了,而是要持續Refine。
  • 要怎麼Refine Design?把Domain的關鍵概念挖出來,呈現在陽光之下,讓Design有所突破:
    • 與Domain專家討論,聽他講系統的角色,角色的行為,資料的流程,獲得的結果,找出之前沒有找到的概念。
    • 深入了解Domain 專家所用的行話,與他/她的對話,字字句句都要弄懂。
    • 在design中有不得不作但不合適的設計時,肯定有什麼隱含概念沒被包含在model中。這句真的很硬
    • 參考Domain領域的書籍,航空、財務、流體力學、遊戲設計,因為Domain專家不是萬能,有參考書更好!
  • Constraint,Processes,Specification三個作法可以協助把關鍵概念找出來
    • Constraint:表達不變量的方式,例如一間教室只能有40人,這就是Constraint。實作的時候為了兼顧集中管理與Design,可以把Contraint放在對應的Class裡面,對應的不變量集中放置在某個檔案裡面。
      • AirPlane包含很多零件,Wing、Engine,Missile,各自的Constraint可以寫WingConstraint,EngineConstraint,MissileConstraint,然後裡面用到的不變量,都集中放在AirPlaneConst裡面。如此可以一眼就看出AirPlane所有的不變量,各自的Contraint可以與各自的Class放一起。
    • Processes:某件事情運作的程序,例如公文處理,主辦人先簽名,給直屬主管,再給其他部門主管,再給總經理。最適合作成Service的形式,如果與Domain專家討論出來的The Ubiquitous Language有明確講出這個Process,那就要明確的把它用Service作出來。
    • Specification:Business Rule的限制,跨Entity的限制,用來測試一個Object是否滿足某些標準。例如一個三年級學生要修90學分才能升上四年級。這就是一個Specification。
      • 簡單的Specification可以寫在Entity裡面;會牽扯到好幾個Service與Entities的複雜Specification,就應該抽取出來變成一個Object,將邏輯集合起來,不會四散在各地。
      • 而這個Specification不一定要跟著Entity走,可以單獨拿出來用,Service內可用,Repository內可用,Applicaiton Layer也可用,寫起來麻煩可以用AOP包裝。

Preserving Model Integrity
了解如何把Model找出來,如何用Model作Design,接下來就用這個Design來實作,每一個迭代就Refine Model與Design,從此王子與公主就過著幸福快樂的生活?
  • 最簡單、理想的狀況是保持單一、設計良好的Model
  • 當有多個團隊在作同一個Project
    • 大家平行寫Code
    • 每個團隊負責Project的一部份
    • 每一部份大部分都不是獨立,多少都有相關
    • 分布在世界各地,只能用英文溝通!
    • 不見得大家可以同時把資源用在完成自己的一部份上
  • 現實世界就是,在大型專案中,保持單一Model很困難,也不是一定要。
  • 多團隊開發不需勉強保持單一Model,可以分成多個Model,並遵守一定規則,就可以保持系統完整性。
如何分成多個Model,又要能夠保持整合Model後系統的完整性,就是用以下這張圖
Model Integration Pattern


Bounded Context
既然要切分Model,那Model要切的多小?最好切到可以指派給一個Team,方便作最有效率的溝通。什麼是Model Context?在DDD就是指Model所要被實作的外在環境與內在狀態,團隊有幾個人、誰是老大、用什麼開發流程、這個Model實作出來會被誰使用、使用語言、Codebase、Database Schema、開發環境,Coding Standard,Release方式等等。清楚的劃出一個界限,然後在開發期間不要越過這個界限,也不要讓外面的人或事進入這個界限。小專案小團隊就不必這麼複雜,但人一多,花在溝通的成本很高,如果在美國、中國、德國、日本的同事在同一天改同一個Class...這時候有Bounded Context才是明智的選擇。

範例:要作一個e-commerce的web application,提供註冊帳號、記錄使用者資訊、使用者登入、瀏覽商品、下訂單、把訂單送給出貨中心、產生各種販售報告、分析報告。
  1. 一開始的Model就是作一個具備所有功能的Web Application
  2. 發現賣東西與產生報告,似乎沒有太大關係,只有讀寫同一份資料。
  3. 建議分成兩個Model,一個是e-shop,一個是report,兩個Model可以分別開發,甚至可以作成兩個不同的Application。
  4. 需要有一個Messaging System來通知出貨中心,e-commerce只需送一個包含Value Objects的message到出貨中心。
可以看到以上設計流程是先規劃好Web與Report二者的Model,再來看有沒有需要分成兩個Application。Model先,實作方式後。

在4.的Value Objects不是訂單,訂單該存去資料庫。只是一個Notification,送出後就不會改變,也無需進資料庫,所以是Value Object。

Continuous Integration
  • 不只是一般Code層級的CI,包含Model本身的CI,Model建立之後,會被改變,需要有作為來保持Model的完整性
  • 當多人在同一個Modle上作業時,Model就會有破碎的傾向
  • 如何維持在Bounded Context內的Model完整性
    • 新概念整合進Model的流程
    • Code Merge的流程,小Team每日Merge
    • 自動build
    • 自動測試
  • 每一個Bounded Context都需要作Continuous Integration,以日為單位。
Context Map
  • 大型的的產品會有多個Model,多個Bounded Context,一個Bounded Context一個Team。
  • Context是一份文件,描繪出不同的Bounded Context的關係,可以是文字敘述,可以有流程圖。
  • Common practice of design
    • 定義Model
    • 做出Design
    • 劃分Bounded Context
    • Create modules
  • 不同Bounded Context間的關係接下來描述
Shared Kernel
  • 兩個Team或多個Team之間分享部份的Domain Model,Code,Database Design。
  • 實作速度最快,同樣的事情只要作一次,要拿資料直接撈。
  • 相依性最高,在Shared Kernel隨便改個東西都會影響到別人。
  • 要改變這些有共享的Kernel,都必須作團隊之間的溝通。
  • 也要作Continuous Integration,不過頻率會比單一Bounded Context內部來的少,以周為單位。
Customer-Supplier
  • 應用在一個A子系統單向依賴另一個B子系統,此時A是Customer,B是Supplier。
  • 以上列e-commerce的範例,分為兩個model,一是e-shop,一是report,report可以是Customer,e-shop可以是Supplier
  • Customer提供需求,Supplier接收需求,排定計畫實作。
  • 需要有一個Conformity test suite把提供給Customer的Interface寫成Test放到Supplier的Test Suite裡面,這個Test就是Acceptance Test,由Supplier與Customer一起決定。
  • Supplier有這些Acceptance Test,就可以放心改功能。
Conformist
  • 當C、S兩個Team有Customer-Supplier的關係時,由於利害關係不同,時程不同與其他因素,S Team可能沒辦法提供完全符合C Team需求的內容。
  • 很多人都是好人,很多人都是想作事情的人,但是每一個人又有自己的難處。
  • S Team也許有幾個好人,願意提供C Team界面與資料,但C Team如果根據這幾個好人的一念之仁下去作設計,反而風險很大。
  • 如果Supplier不給足夠的support,Customer就自己搞,用Separateed Way,這也是一種實務做法。
  • 如果S Team提供的內容有限,但還是勉強可以用,可以用Anticorruption Layer包裝起來,隔離S Team不良的設計。
  • 如果S Team提供的介面與資料完整,那就是Conformist出場的時候,C Team可以完全使用S Team的Model來作設計,這有點像Shared Kernel,不一樣的地方在於,Conformist的情況下,C Team是完全不能更動S Team的Model。
Anticorruption Layer
  • 在界接舊的應用程式,或是界接其他團隊、其他公司的產品時,拿出來使用。
  • 舊的應用程式,Model可能與新的完全不同。
  • 其他公司產品的開發者,根本沒機會溝通,只能看看文件。
  • 界接方式:
    • 透過網路通訊協定
    • 資料庫,超危險,因為放在資料庫的資料沒有任何Model可言
  • Anticorruption Layer形成一道防護罩,用來作內部Model與外部Model的轉譯。
  • 用Service Pattern來設計Anticorruption Layer,就是內部沒有狀態,只有提供方法
  • 用Facade與Adapter來實作
  • one Service, one Facade, one or many Adapter. 對於其中來往的Object,可以有Translator把要送到外部的Object包裝成外部系統看得懂的形式,反之亦然。
  • 不應該有Business Logic,只有轉換邏輯。

Separate Ways
  • 多個團隊在實作多個子系統,需要花很多時間在整合Code,整合測試,或是互相實作其他團隊需要的介面。當這些交互合作、整合太過於花時間,複雜度太高時,就可以考慮Separate Ways。
  • 先決條件當然是多個子系統的Model可以作出沒有相關的設計。
  • Model不同、程式語言、執行平台都可以不同。
  • 可以有共通的使用者介面,例如一個Web Page,不同的連結一個導向Tomcat的Java Web Application,另一個連結導向Node.js的Web Application。
  • 使用了Separate Ways後,分出去的子系統基本上是回不來的,所以要作這個設計決策時要三思。
Open Host Service
  • 當很多subsystem都需要存取外部System,每個Subsystem都有一個Translator來處理資料與流程的對應,很容易就產生一堆重複且複雜的Code。
  • 對於這個外部System,使用一個Open Host Service來對應內部Subsystem的需求。把Protocol設計的儘量通用,Desgin當然是用Service Pattern來設計(也就是內部沒有狀態,只有Operation)。
Distillation
  • 目標就是蒸餾出Core Domain
  • 大型系統就會有大型Domain,大型Domain就會有大型Model,就算是作了多次Refactor仍然保持巨大體積與複雜度。
  • Distillation就是蒸餾出Core Domain,與附產品的Sub Domain
  • 把通用的流成與資料分離成Sub Domain
  • 以之前介紹過的Air Traffic Controller為例,用來作尋徑的Simple Routing System雖然有用,但是不是Air Traffic Controller這個系統的目的。所以在Air traffic controller裡面就是一個Sub Domain。
  • Air traffic Controller真正的Core Domain就是飛行路線預測,避免飛機相撞。
  • 找出Core Domain有什麼用?花最多的心力設計與實作,作出有彈性可以滿足現在對應未來的系統。
  • Developer覺得Domain做完就沒用了,最新的技術才是Developer感興趣的東西。
  • Core Domain對產品而言才是最重要。這與追求技術相互矛盾,但是成功的專案都有找到平衡點。
  • Core Domain不是一次就可以找出來的,要透過不斷的Refine與Refactor。
  • 分離出Core Domain與Sub Domain,還有一個用處,可以考慮用簡單的方式來實作Subdomain:
    • 外面有在賣或是Open source的Solution,要考慮學習曲線、可維護性、導入難易度。
    • 以外包的方式,還是會比內部開發的子系統難整合,要把與外包子系統溝通的介面定義完整。
    • 使用Existing Model,真要自己作Sub Domain的話,設計部分可參考已經存在的Model,Design Pattern,Pattern-Oriented Software Architecture,Patterns of Enterprise Application Architecture,Enterprise Integration Patterns等等都可參考
    • In-House Implementation,自己做最好整合,不過就是要時間,也要花費精力維護。

作者訪談使用DDD的重點
  1. 保持親自動手,Modelers也是要寫Code的。
  2. 專注在真實的運作情節,抽象化定要根據在真實會發生的Case來設計。
  3. 別想把DDD套用在所有地方,畫出Context Map來決定要把DDD套用在系統的哪個部分。也不用擔心越界,再重構回來就好,只要有對應措施,軟體可以是軟的。
  4. 多作實驗,預期會犯錯,Modeling是一種創造性的程序。

2013年11月2日 星期六

筆記:20131030 Unity原廠講師大解密

Asset Bundle工作流程及人物換裝實例 - 劉剛
  • Unity內部的資源有兩種(斯斯有兩種?):
    • Resources:有10年歷史,儘量用Asset Bundle
    • Asset Bundle:暱稱AB,現今Unity處理資源的中心
  • AB選項的差異
  • 透過Web Player下載Asset Bundle的Cache需要收費,在iOS,Android的Cache不需收費。--->手持遊戲卯起來用!!!
  • 瀏覽器本身也有Cache,為什麼要用Unity的Cache?
    • 因為存在Cache裡面是解壓狀態,下次調用不需再解壓一次,大省CPU時間。
    • Unity會管理Cache大小,壓縮檔案解壓後會丟掉。
    • Browser就只能存壓縮狀態的Asset Bundle,而且位置每種Browser都不同。-->有付錢有差
  • AssetBundle.CreateFromMemory:大多數用在對內容作加解密,無法Cache,也就是每次執行都要下載一次,Script也可加密
  • AssetBundle.CreateFromFile
  • AssetBundle.Load與Application.Load的差異,Application.Load只能Load StreamAsset裡面的東西。
  • 每開一個www用來下載AssetBundle,就要用掉8MB的Buffer,所以www下載完要作以下動作來清掉那8M:
    • WWW.Dispose
    • AssetBundle.Unload(false)
    • Resources.UnloadUnusedAssets
  • AssetBundle可以作Dependency!!!
    • BuildPipeline.PushAssetDependencies,把資源推進去
    • BuildPipelinePopAssetDependencies,把資源彈出來
  • 假設Push以下資源:A,B,C,透過Push&Pop,可以產生3個Packege,C依賴B,A,B依賴A,A獨立的Asset Bundles,完全不會重複包資源!
  • 在一個Prefab裡面會有Mesh,Material,Shader,Texture,Script,都應該分開包,Load的時候再按照順序Load回來,再配合上Cache機制,已經下載過的Asset Bundle就不需再下載一遍,可以達到資源、頻寬的最佳使用-->不過這個資源相依的邏輯必須要設計清楚,否則會很亂
  • 官方的實務建議是每個Asset Bundle在1M左右,太小下載次數會過多,太大下載時間過久。
  • 可以把Script放在ScriptObject裡面,再透過Asset Bundle載回來,如此就不需一定要將Script放在第一包。
  • 官方Demo案例,透過Dependency的使用可將131MB大小縮成8.2M!
  • 角色紙娃娃的處理方式,可以參考Unity提供的Character Customization範例,看裡面如何拆分Asset Bundle。
  • 地圖處理方式,可以把地圖拆成多個子場景,在主角移動時即時下載,即時顯示。
  • Asset Bundle有CRC機制,如果下載有問題會自行重新下載
  • Code安全性
    • iOS平台封閉,不需擔心(JB就再說)
    • 其他平台可用Asset Bundle + 把Code作成.NET Assembly + AssetBundle.CreateFromMemory加解密來作
    • Web不適用於把code包成Native DLL的形式

大規模場景的資源拆解和動態載入-張鑫
  • 進入場景全部載入,耗時,玩家根本沒走到
  • Unity場景只有支援4096X4096,單位公尺,沒辦法作更大的場景
  • 拆分有兩種:
    • 地形資源拆分
    • 地表拆分
  • 推薦拆分工具:Terrain Composer,http://www.terraincomposer.com/,最大可以拆8x8,每塊大小2000x2000,也就是16000x16000,也就是16kmX16km
  • 如果地形早就已經編好一堆了,該怎麼辦?自行拆分
  • 地形資源怎麼拆?先生成空的NxN的Terrain
    • Terrain Data:把Data放到對應分塊-->大工程
    • Light map:用Free Image來拆light map的.exr檔案,Free Image有提供C++ Native DLL
    • LOD:重新把每一塊Terrain作Terrain.SetNeighbors
  • 地表資料怎麼拆?
    • 根據地形分塊來拆
    • 用之前提到AssetBundle Depedency的方式打包
    • 一包在1M左右
  • 拆好了, 如何顯示?
    • AssetBundle太大,在進場景就先Load
    • 攝影機移動時,載入以攝影機為中心那一個的九宮格,其他的就釋放掉,確保記憶體使用量,兼顧效率。
    • 避免頻繁的Instantiate,Destroy,造成拉機(?)回收機制太頻繁(平均要10幾ms),可用Object Pool。
    • 避免開多個www同時下載,耗記憶體,www是使用Thread,會加大系統負擔。
    • Coroutine也不要開太多,官方建議一個Coroutine循序下載多個Resource
  • 載入時間比較:
    • Shader,Material:載入時間最久,主要在Parse內容進GPU,建議先載進來,反正不占記憶體。這也是Demo案例1xFPS到5xFPS的關鍵。-->第一次提到
    • Texture,Particle
    • Mecanim,Audio
  • Object Pool範例請參考AngryBot裡面的子彈
  • 現場Demo拆分後的場景,畫面精細度很好,地圖超過4096x4096,飛機一邊飛一邊動態下載AssetBundle後顯示地形,FPS在50以上,在主流手機上也順暢。

使用Unity引擎開發3D網頁遊戲 - 劉剛
  • 改Loading Unity顯示的Logo,unityObject.embedUnity,設定parameter for 3.4
  • UnityObject2(params),改params for 4.X
  • 要彈出新視窗,OpenUrl不行,ExternalEval不行,要用html的iFrame/div,也可用rokbox,http://www.rockettheme.com/extensions-joomla/rokbox
  • Unity對DB,用WWWForm作Post,用WWW作GET-->直接讀寫DB有點瞎
  • Web遊戲內容保護:
    • code加密:把Code包成Asset Bundle後,存成TextAsset,再包成AssetBundle(For Cache!),取用時要用Reflection取用,極難用。
    • code混淆:推薦作法,Web平台要官方幫忙,Android可以自己作,iOS聽說不用作
    • 資源加密:最需要的是Texture,可加密後存成TextAsset,記得再包成AssetBundle才能用Cache。
  • 記憶體Leak,4.1.2的Profile可以看到所有GameObject的狀態,請多用Profiler
  • 動態載入,用ScriptableObject定義場景描述檔,記錄Dependency,然後根據這些資料來載場景內容。
  • Coroutine在Unity不是Multi Thread,多用還是會拖Main Thread,請慎用。
  • 在一個新場景或新關卡,把Material與Shader先Load出來,放到一個空的GameObject裡面,然後Diactive,有需要在放到正確的地方。-->第二次提到
  • AssetBundle打包顆粒度,Debug可以包越小越好,Release建議1MB
  • 封住右鍵功能,防止使用者更換Unity Player版本。
  • 從放遊戲的Web Server上取其他URL的資源,要設定CrossDomain.xml
  • 如果不用Asset Bundle,自行作壓縮解壓縮,一定要在載下來的時候馬上解壓縮,而且用AssetBundle.CreateFromMemory,耗時。-->AssetBundle的確是要Load才需要解壓...
  • Material,Shader先打包吧!-->這應該算第三次提到吧?因為很重要所以要講三次??

Unity的網路解決方案
  • 非常好睡zzzzz
  • Photon Server:C#界面,底層用C++實作,可負荷30K同時在線,Load Balance機制,有FPS範例,用TCP與RUDP,只能用Windows。
  • SmartFox Server, http://www.smartfoxserver.com/,Java實作,可用Windows/Linux,沒有Load Balance,提供多種監控與管理界面。
  • 用RakNet自己作

Unity引擎的性能優化 - 張鑫
  • Mobile優化
    • 300~2000個Polygon
    • Skinned Mesh Renderer同時最多一個
    • Texture數量同時1~3
    • bone數量<30
  • Static Object
    • < 500 poly
    • 標記Static
    • 不要用Animation
  • 地形:Texture數量<4,用Texture融合,可加5fps~10fps
  • Texture格式
    • 用png, tga
    • 不要超過1024
    • 看起來像就好,可以用128x128,就不要用256x256
    • 用mipmap
    • 控制UV範圍在(0, 1)-->原來還可以超過?
  • Mesh簡化,用MeshLab,但只能簡化靜態Mesh。用Simplygon,
  • Mesh簡化,Animation簡化,用Simplygon,http://www.simplygon.com/,要錢,效果好。
  • 不要用System.xml用mono.xml,減少程式碼大小
  • Forwarding Light原理,要draw的Object數量X光源數,所以光源越少越好。
  • 把Pixel Light設定固定值,美術再多光也只有一個。
  • 粒子在Mobile平台,是以Block為單位Render,太多粒子會加高像素填充率,因為粒子沒有深度,所以一定要畫,Mobile平台的功率不夠就會大大拖效能。
    • 粒子小一點
    • 粒子數量<50
    • 粒子不要有Alpha
    • 粒子不要碰撞
  • 儘量不使用Mesh Colider,用Box,CapsuleCollider
  • Animation部分
    • 沒需要不用Animation,不需要每個東西都會動
    • 沒有縮放,手動或程式作業把Scale Curve去掉,可以減少33% Blending作業
    • Mecanim是多緒執行,不會拖Main Thread,一個Game Object上多個Animation clip才有用,如果只有一個clip,可考慮只用Animation
    • 用Body Mask告訴Mecanim哪邊不需要計算
  • Renderer部份
    • 避免Alpha Test,Alpha Blend
    • Static Batching需要大量的VertexBuffer,如果Vertex過多,要把一堆Vertex搬來搬去,反而拖效能
    • Dynamic Batching在Polygon < 900會自動作用,如果GameObject的Shader有Position,Normal,UV,每多用一個,900就要減半。
    • 在使用Dynamic Batching時,Vertex只能在CPU作結合,所以Vertex不能太多
    • 如果同一場景中小塊Texture很多,可以考慮用程式將Texture拼合,要拼合的Texture如果UV超過(0, 1)就很難作。
  • Mobile平台的Occlusion Culling適合用PVS and dynamic objects
  • 用for代替foreach,因為foreach會生一堆Iterator
  • 用Struct代替class,快30倍
  • 傳說中8小時一定回信的高級email support
  • 影子的優化,可參考ShadowGun模擬影子的實作
  • 接下來Unity的Roadmap會支援新UI(講好久了)與Server。

2013年9月10日 星期二

李小龍與敏捷開發

http://www.csdn.net/article/2013-09-03/2816811-Agile-development-JIRA-Atlassian

X的,這篇真的太妙了,JIRA不過是一個isssue tracking system,扯到敏捷很合理,因為JIRA內含可以執行敏捷流程的設計,不過扯到李小龍,還真是拉的非常遠!不過講的其實很有道理~

When one has no form, one can be all forms; When one has no style, he can fit in any style.
無招勝有招、這不是說不用去學招式,而是學了招式要體會它的意義,不要被招式所侷限。所謂設計範式與一些程式技巧就是我們搞軟體的招式吧,當學了夠多招,而且體會它的意義的時候,就不會拘泥於用哪一招,雖然很難想像寫code時像水一樣變幻自在的感覺,不過聽起來蠻有道理的!菜鳥如我,透過這段話至少知道如果學的招在戰鬥時沒辦法使用,就爽快的不要用吧!

All fixed set patterns are incapable of adaptability or pliability. The truth is outside of all fixed patterns.
Scrum、XP、Lean、RUP是軟體開發的戰略Pattern、Design Pattern是軟體開發的戰術Pattern、Refactor,Language最佳語法是軟體開發的接近戰Pattern,不同領域都有不同的Pattern,每個都有它的界限,真正使用起來,限制還不小,那要學嗎?學!當我們進到Pattern裡的時候,才有機會看到Pattern外面的The Truth!

If you spend too much time thinking about a thing, you'll never get it done.
需要想很多的複雜方案,直接就送去領便當!附帶一提,看到這句話,讓我困擾多月的問題得到了一個清晰的答案!搞軟工與寫程式也是一樣,如果要弄一個遙遙無期的架構,不如以一個可接受的形式先把User Story作出來,把bug處理掉才是王道,再不行,把User處理掉,那就是霸道!不管是王道還是霸道,就是要簡單~

Make at least one definite move daily toward your goal.
每天都向前進一點點,重點不在Move有多少,重點是在想法的累積,想法就是一種能量,能量形成質量,質量就可以改變物質宇宙。物理學是這麼說的。敏捷開發小步推進的方式,就是可以讓個人與團隊凝聚我們正在前進的想法。

I fear not the man who has practiced 10,000 kicks once, but I fear the man who has practiced one kick 10,000 times.
無聊的重複帶來最強的作用就是確定性,還記得我練習破萬次的假動作,過了不知道幾次的彎,真正的一閃,總是會在重複的淬煉下出現。短時間的iteration,就像一塊磨刀石,反複的磨,刀子就會越來越利。寫code接近戰也是一樣,遇到不懂的,就把它弄懂,遇到不熟的,就再把觀念複習一次。遇到很熟的,還是再一次。我們不求熟悉全天下所有的技術與演算法,只需要把有用到的技術,磨到最利就好。

一次屁這麼多,其實我也沒很喜歡李小龍,不過他在武術方面的哲學還真的有一套,連我隨便看看都相當有感,難怪老外那麼愛他。雖說我們搞軟體要不斷學習,不過現在技術那麼多,其實有些東西真的是出來鬧的,如果像我分辨不清楚,就挑自己有興趣,用的到的就好啦,這也是把事情簡單化的一種方式~

2013年8月21日 星期三

工程師的等級

看到浪潮之巔裡面敘述Google對人才的需求標準,一流的工程師能作10個二流工程師的事情,二流工程師能作10個三流工程師的事情,也就是說,一個一流工程師可以作100個三流工程師的事情,更重要的是,三個臭皮匠勝不過一個諸葛亮,根據一直工作到現在的經驗,我自己證實這是真的,那我在哪一流呢,所以有了以下分析。
  • 不入流的工程師:連自己遇到的問題都沒辦法好好解決,只能作一些日常維護或簡單開發工作。剛出社會, 或是出社會很久但是都作不好的人。
  • 三流的工程師:可以處理自己遇到的問題,但是不想,或是沒有能力,持續讓自己的能力或是開發的系統成長進步。我看到大部份的工程師都屬這類,會抱怨,但不主動學新東西,會有一些想要進步的想法,但是礙於自己的技術與堅持不夠,能作到的有限。
  • 二流的工程師:能夠持續的進步,學習新技術,不斷的改善自己開發維護的系統,應用新技術或新觀念到自己的架構上。我以及在Ruckus的工程師大部分就屬於這個區間,可以積極的學習與使用各式各樣的技術來處理問題,而且可以規劃比較完善的架構,但英文好不好還是會有一個程度上的差異。
  • 一流的工程師:自己就可以作新的技術,新的框架,新的標準,環境沒有就生一個出來。能夠主導大型Open Source專案的人,或是在各公司主導規劃架構,設計創造新架構的人。
  • 頂尖的工程師:進Google,Microsoft,Blizzard,等世界級軟體公司。或是創造能夠影響地球的系統。大師級人物,不用多說,能處理很低階的系統細節,也能開發改變世界的系統,或是寫出可以成為人類進步基石的演算法。
今生今世,或許沒辦法到頂尖,一流也應該沒問題吧!話說回來,其實第幾流好像也不重要,不過是拿來茶餘飯後閒聊一下,能夠幫助自己的公司,作出叫好又叫座的產品,才是最重要的目標!

2013年4月9日 星期二

筆記:版本控制Git

distributed even if your workflow isnt
distributed is the new centralized.

Git靠在檔案裡面記錄的內容來維護自己的狀態,Daemon看使用者的位置在哪個容器,就對哪個容器作事情。

目錄名稱有.git就是裸容器
  • 用檔案內容經過SHA1產生唯一ID,長度為160bit,只需比對ID,就可得知檔案是否相同
  • 用ID的前16bit做目錄名稱分配,避免同一目錄檔案過多,造成效能低下。
  • 160bit的SHA1 ID不只用在內容,Git內部的資料結構,全部都使用SHA1,blob,tree,commit,tag,全部都有自己的SHA1 ID。架構簡潔,容易了解。
  • 整個系統的資料結構,只有blob、tree、commit、tag,非常簡單。
  • 同一個檔案,如果做了更動,ID就會不同,也會將內容存成一個新的blob,相較於其他SCM是記錄檔案變動,較耗空間。所帶來的好處更多?
  • 不以檔案系統的檔名與路徑來決定內部管理資料結構,避免與檔案系統內容綁死,像是改名,追蹤歷史,都以ID來判斷。
  • 只要檔案內容不改變,不管是更名,分支,搬移,複製,全部都不需要再多一份檔案。
  • 透過維護Tree Object把所有git相關設定放在容器根目錄底下,避免四散設定檔
  • 沒有所謂daemon或是Servcie,全部都是有需要才執行功能,功能執行根據容器的內容來處理。這樣省記憶體,省CPU時間。
  • 本來想說要是檔案有好幾百萬,那光是開個tree就會很耗記憶體,搜尋與比對檔案內容是否相同,也非常花時間。Git的tree裡面可以有tree,所以tree裡面的內容會因檔案的樹狀結構而分散,除非極端的狀況,同一個目錄就幾百萬個檔案,不然一般的使用狀況,tree檔案的內容應該都很小。
  • 是不是多個專案都應該放在一個容器裡面?在Ruckus用Perforce是這麼做,在網龍用Subversion是分開,在Git裡面應該是?因為在Git裡面的branch都是以容器為單位,commit物件也是追蹤容器,所以如果多個專案在同一個repository內,沒有辦法區分出各專案的歷史。而Perforce與Subversion都是以檔案為單位,可以用資料夾來區分歷史,所以可以把多個Project放在同一個Repository裡面
  • Git以內容來區分,是否有可能做跨容器的Merge,Merge好之後可以找到某個檔案在兩個不同容器的Branch的所有歷史?

CH4 基本的Git概念

四個基礎物件:
  1. Blobs:所有資料都是Blob,用SHA1演算法來產生唯一的資料代碼,用來識別資料,比對檔案是否相同,與對資料定址。
  2. 樹(Tree):代表與處理目錄資訊
  3. 送交(Commit):代表與處理送交,容器內的歷史就是由送交所組成。
  4. 標籤(Tag):代表特定的物件,大多是送交物件
Git就用這四個物件,來建構出容器所有的歷史,並且讓重複的內容,只佔用一個空間。
四種物件都會有自己的SHA1雜湊值
索引(Index):Git送交之前的暫存狀態,在送交之前可自由修改。

CH5 檔案管理與索引

Git將檔案分為三個群組:
被追蹤:顧名思義
被忽略:不需管理檔案,*.obj,git管理用檔案.git/,使用者可以自定
不被追蹤:要被處理的檔案。

CH6 送交
每一個送交都會指向前一個送交,經過一段時間,一連串的改變可以用一系列的送交來表示,在送交的時候,必須要跟之前的送交比對有多少改變,這件事不需每一個檔案都作比較,只需比對相對應的Blobs與子樹物件即可,如此樹與樹比對的作法,大大減少遞迴比對到每一個檔案的計算與時間成本。
ref:參照至某個Git物件儲存中物件的SHA1雜湊辨識碼
symref:指向某個Git物件的名稱,也稱作符號參照
dev分支名稱是symref,參照到refs/heads/dev,這是個ref

Git的ref有三個命名空間:
  1. refs/head/ref:本地名稱
  2. refs/remotes/ref:遠端名稱
  3. refs/tags/ref:標籤
可以看到,對於Git而言,只有本地,遠端兩種分別,不管遠端在哪裡,不管遠端扮演什麼角色,沒有其他邏輯,這也是一個分散式系統實作的思維,簡化處理與遠端的互動。

有四個特殊的symref
HEAD:指向目前分支最新的送交
ORIG_HEAD:Merge或Rebase時,目前分支的HEAD
FETCH_HEAD:git fetch下來的分支的HEAD,只有在git fetch之後有效,處理完就沒了。
MERGE_HEAD:Merge時,外部來源分支的HEAD

HEAD~2:HEAD的父親
HEAD^2:HEAD父親的兄弟

Git使用DAG(有向非循環圖)來組織送交

CH9 合併
合併必須發生在單一容器內,也就是說,所有要被合併的分支都必須在相同的容器內。分支如何出現在容器內並不重要。遠端的分支,要作合併也是把分支從遠端下載回來,然後再與本地分支作合併。-->這個作法提供了一個分散式系統實作的思維,單一系統不在遠端作操作,而是把資料從遠端抓回來之後再處理,邏輯就是以本地邏輯,這樣可以簡化系統的複雜度。

合併過來就產生一個新的送交,來表示新的統一狀態。

處理合併的方式,就是沿著送交歷史,比對送交物件,在這邊,SHA1雜湊代碼的功用又出現了,不需一路比對到每一個Blog,只需比對送交物件的SHA1雜湊代碼就可以知道是否為相同送交。

退化的合併:
  1. 已是最新:我有動,其他人沒動,當作是合併的終止條件。
  2. 快轉:我沒動,其他人有動,把所有外來的送交加到我的HEAD上,然後修改HEAD指到最新的送交。

CH10 修改送交
未發布的送交記錄可以隨意修改,已發布的送交記錄請勿修改。這也是設計分散式系統應該要遵守的原則,各自獨立,一旦有互動就要遵守約定。
我目前最理解可以使用Rebase的點,就是當自己在分支上進行開發,產生了一連串的送交,然後從中央容器

CH11 遠端容器
Git記錄容器內所有分支的歷史記錄,完全不用向Server查詢,用磁碟空間來取得容器的獨立性
Git複製出來的容器與原始容器完全對稱,所以可以獨立自主。-->分散式系統的一個點應該與其他點完全對稱,可以獨立處理所有的事情,但也可以互相協調讓輸出量更大。
Git的本地分支可以設定遠端追蹤分支,只要送交物件相同,就可以作合併與rebase,提供使用者彈性。
Git用email地址識別唯一使用者,認證的部份交由外部系統來作,使用者管理系統每個環境都用,Git不需再作一個。而且因為email地址是全球唯一,所以在沒有中央Server的Git環境中,也能辨識出單一使用者。
裸容器的概念是為了要整合最終結果,一個專案可以不只有一個裸容器,可以因為政治,技術,地理,任務等等方式,幾個容器的擁有人共同協商出一個裸容器。

CH12 管理容器
Git的分散式開發模型可以使用中央管理容器,每個開發者仍然可以擁有私人的容器拷貝並能夠獨立作業,工作是分散的。
Git沒有唯一真實的歷史記錄,只要有測試過,push/patch進來,結果正確就可以。所以送交物件,只會有先後順序,時間是第二順位。換言之,時間以及空間的操作都不是好選項,所以Git必定考慮到量子物理的影響。-->分散式系統地點分散,作業流程分散,要求時間同步當然也不可能,Git操作的是資料,所以一切以資料的順序為主,這也是可以應用在分散式系統設計的觀念。


CH15 結合專案

https://projects.kde.org/ KDE專案列表說明
http://quickgit.kde.org/ KDE所有專案的容器,可以參考它組織整個KDE產品的架構
http://techbase.kde.org/Development/Git KDE從SVN轉到Git的官方資訊
http://www.omat.nl/2010/10/03/git-kde-org-available/ KDE從SVN轉到Git的相關資訊

要在容器裡面匯入其他專案的函式庫,在Git裡面有以下解法:

  1. 使用拷貝來匯入子專案:如果是外部函式庫,且不會在開發過程中有任何改變,或是很少的更新,外部函式庫的歷史對容器並不重要。
  2. 使用git pull -s subtree來匯入子專案:會從子專案中合併整個歷史記錄,好處是當子專案在外部被更新,本地容器也可以透過git pull -s subtree來更新,還可以保留子專案的歷史記錄。但是如果要把對子專案的修改上傳,邏輯上就會破壞子專案本身自己的歷史記錄,這個結果子專案的開發者沒有辦法接受。
  3. 使用客制化腳本程式來取出子專案:在容器內git clone子專案內容,然後忽略子專案在容器中的內容,讓專案可以編譯,如果想改子專案內容,進到子專案目錄,就是另一個容器。如果需要修改子專案內容且push回去,也是可以的,但是遇到分支,就又需要手動處理一次。有人已經寫出自動管理在容器內的子專案容器的Script。
  4. 原生的解法,使用gitlinks與git submodule:


CH16 在Subversion容器上使用Git

需要把SVN當作是Git的中央容器,有點違背Git分散式的原則,但沒差。
一個容器可以下載所有分支,也可以一個容器一個分支

使用Git svn,可以離線工作,查詢記錄的速度也更快。
使用Git svn,可以減低SVN Server負擔,加快整體開發速度。
使用Git svn,可以作到在SVN的環境下,沒有人知道你在使用Git。

2013年4月6日 星期六

筆記:Perforce The Flow of Change

描述軟體開發的理想世界

在理想世界我們只有一個版本,沒有bug,有足夠的時間開發,開發不會延遲。但是現實世界並非如此,所以我們有了軟體版本控制系統,我們有了分支。

限制分支的數量,把分支看成一個一個stage(development、QA、Beta test、Live),來處理短周期Release,避免分支過多,不管Release再怎麼短,Release Branch只會有三個(所以並不會限定development branch),這個作法稱為staging codeline。

從mainline中分支出development分支,來處理不同開發者,不同團隊開發延遲或是錯誤的程式碼造成無法Release的問題

除非開發者的成果可以正常編譯且功能正常,否則不會進mainline,達成mainline可以維持在理想狀態不斷前進

用Map(地圖)、Protocols(協定)、Convention(公約)、Etiquette(禮讓)來讓Branch可以被管理,
Ex:道路

codeline=觀念
branch=實作
codeline分支出來的起源稱為baseline
沒有baseline就稱為mainline
aka=also known as


tofu scale:越上面越硬(可測試,接近Release,也就是穩定),越下面越軟(不穩定,離Release遠),這個等級代表改變codeline的風險程度。mainline在中間的位置,每個codeline的tofu scale相對於mainline,注意:親子codeline可以表示相對硬度,兄弟codeline只能跟baseline比較

staging codeline與tofu scale在mainline上合體,形成可以兼顧Release與development的分支策略

以map方式表達分支狀態,處理兄弟codeline表示的問題,表達Change該如何流動

使用Change Flow的策略,完成把所有Change Flow全部匯流到mainline的理想,所以mainline只會穩定的成長

Change Flow只能在codeline與它的baseline之間流動,所以一個codeline的改變只能流到baseline,基本上不能跳著流動

只接受穩定的改變,決不強加不穩定的改變

一但relase line被改變,即刻把change整合進baseline,因為release line的改變就是bug fixes and patches,所以會讓baseline更穩定。

絕對不能從baseline把change送進release codeline,因為release line有被更多的測試,所以更加的穩定,而baseline的change會造成release line的穩定度下降,這個原則經常被違反。

change應該被持續整合到development codelines,可以帶給development更多的穩定度,而且可以反映最新的狀況。

development codeline不是持續整合到baseline,而是分批整合到baseline,要整合到baseline,必須滿足以下條件:
     要實作的功能都已實作到一個好的點(Point of completion),實際上可能還有bug,只要不打亂baseline就OK,
     通過development line自己的test
     baseline要是正常狀態(不能有build is broken)
     通過baseline的test

module:一個包含檔案的目錄架構,檔案的內容就是軟體的一部份,可稱為source tree、component、subsystem、folder,module裡面的相對路徑是該元件能夠正確運作不可或缺。

codeline就是module的集合,通常mainline會包含所有的module,分支出去的codeline,會包含所有或是部份的module。

module分成幾個種類:
     Active:就是要被開發的module,就是branch出codeline的理由,隨著工作進行不斷改變,會流向baseline或是從baseline流過來,例如正在開發的物品使用功能。而流向的規則就是比較與baseline的軟硬。
     Inactive:拿來作編譯、測試、除錯,並不會隨著工作進行而改變,流向也只會從baseline流過來,例如公司內部開發的數學運算函式庫,從mainline分支過來某遊戲的development codeline之後,就只是叫用功能而已,不會作改變,當這個函式庫開發者作了大更新到mainline上面,而這個更新的功能codeline有需要,就會從mainlien merge過來。如果是外部的第三方函式庫,可能連更新都不會更新。
     Private:是baseline的縮減版,在開發時期會持續改變,但是不會與baseline有任何流動,例如bin/,內容都是編譯出來的,或許包含設定檔,但這些設定檔也是給debug或測試用,不會回到baseline。

根據以上定義,不同的Module與baseline的互動,可以歸納出如下的結果:
Active:只需要merge
Inactive Module:只需要copy
Private Moduel:什麼都不用作

雖然coding&mering會破壞穩定性,但這是一定要作的事情,不然就不會有產品。但是可以把風險集中在某些地方,因此coding&merging應該在soft codeline。

所以要"Merge down, copy up",從Release line到mainline作merge,從mainline到development line作merge,然後development要作好編譯與測試,然後copy回去mainline。此外,在copy回去mainline的這段時間,mainline必須是被鎖定的,這樣會中斷其他開發者的開發?所以流程必須要建立在development line常常從mainline merge過來,而且有畫分好Active module與inactive module,測試也要很快就能測試結束的狀態下。

解決分支複雜度的方式,在本文中就是以下四種觀念:
Map(地圖):就是流動的圖,描述軟硬關係,流向
Protocols(協定):就是baseline protocol,Release line到baseline持續Merge,baseline不能流向Release line。baseline持續流向development line,development line分批流向baseline。
Convention(公約):分成Active,InActive,Private三個Module,"Merge down,Copy up"。
Etiquette(禮讓):進baseline要切出空檔時間,讓不同Team&Member copy回去,其他人禮貌地等待。


心得
公司裡所有的專案與元件全部都會在mainline上面,也就是直接在Perforce的depot下。
只要一分支出codeline,擁有者必須要持續維護,還有作測試,雖然不管是perforce,Git,或是Subversion都可以分支,但是如果是牽扯到共同開發的分支,就必須遵守規則,換言之就是維護成本提高,但是如果可以讓事情變得更好,就算成本提高也是值得。

baseline必須有一套測試機制,最好是自動,如果還要動用到QA,那QA會太多工作,讓QA只處理Release line就好。

development line也必須要有一套測試機制,通常可以把baseline的拿來用。

要使用module來表達軟體,就要先把軟體切分好元件,或許一開始沒辦法一步到位,但是可以透過重構的方式來改善。

問題
用copy或是merge在perforce的codeline會有什麼不同的呈現方式?還是用想法管理?
實際將private copy到mainline,可以看到copy的選項與merge不同,基本上copy就是將branch mapping整個蓋過去。所以為防止蓋到不想蓋到的地方,ㄧ定要先作merge,保證沒改動的地方與mainline完全相同之後再copy上去。完成之後在reversion graph看到的與merge一樣,方向也相同,不過可以看到chang list裡面描述的不同,有copy from。那copy到底有什麼意義,經過思考之後,我的理解是:
  1. Merge可選擇來源codeline的change list要不要進目標codeline,可以看到額外的選擇很多,所以是用在從hard to soft,Ex:從Release到mainline
  2. Copy就是從來源codeline覆蓋到目標codeline,所有changelist照單全收,所以適合將新功能進baseline,也就是從soft到hard,Ex:從private到mainline,如果弄了新功能,卻又不想進mainline,那merge也沒有用。真的發生這種狀況,就是codeline的作用定義不清楚。


怎麼在Perforce上分Active、Private、InActive Module?好像也是用想法分?

DevOps Lessons Learned at Microsoft Engineering 筆記

原文: https://www.infoq.com/articles/devops-lessons-microsoft 筆記 組織 講Microsoft裡面的DevOps 故事描述的是Cloud & Enterprise and the Bing ...