分類彙整:工作

別急著用那些新奇玩意兒

一個正要用PHP開始工作的朋友焦慮地問我:同事說我應該要用NetBeans。它是一種叫IDE的東西?我查了資料,發現它提供很多功能:字詞自動補完、全文搜索…等等。但我裝了之後覺得好難用,壓力好大!

我看了看他的電腦:他用Ubuntu當作業系統、Sublime當編輯器、git做版本管理、Apache當伺服器。這些工具夠他解決幾乎所有問題了。

我問他是否看得懂那些文章提到的「優點」在說什麼?「看不太懂。」
我問他同事有否解釋要拿NetBeans來做什麼?「好像可以設定連線、好像很方便、好像歐洲人都用NetBeans。」

這讓我回想起自己踏入這行後困惑很久的煩惱:為了一個別人大力提倡、但是自己看不出來有什麼用的東西而感到焦慮。

對於這種看到新工具、新觀念、新知識而產生的焦慮,我最後養成了一種態度:隨便看看、知道有這個東西存在就好了。之後遇到瓶頸、困難時再回頭,看看它們能否解決我的麻煩。

別浪費時間煩惱這個東西到底能解決哪個問題了;也別為了這種困擾感到挫折、覺得自己很笨。

課本上的一個名詞、一個Design Pattern、一個時髦的工具、一個軟體開發方法、一個軟體測試技巧、一個抽象化思考方法、一個程式語言的某個語法…。看不出來有什麼用,就別急著用。

時間一久,你還可能發現一件更難相信的事情:你從頭到尾都是對的。

那些新奇的狗屁也許根本就有某些缺點。你第一眼就看到了,但是所有人都不去提。

國王裸體在街上走!你的眼睛說不定從來沒看錯。

我不是在說學習沒有價值,我想說的是這兩件事:

* 碰到麻煩再去學習進階的,很多時候這樣才學得透徹。

* 所有東西都可能有缺點、或是只在某些情況適用,也許你只是很早就察覺缺點。

我再換句話說吧:

當你為了新奇玩意兒感到焦慮的時候,

或許,學習它的時候未到;

又或許,那東西真的沒什麼屁用。

(Photo via Alessandra, CC licensed.)

PHP這個程式語言

PHP是web領域的知名程式語言,沒有資訊背景的人可能也聽過這個名字。

不像其他語言在設計上有所堅持,PHP只堅持找到解決web問題的最短路徑

這個語言的內容雜亂,會飽受批評完全可以理解。它除了專心「把事情搞定」之外,幾乎什麼都不管。

我從事PHP開發以來,發現它門檻低、使用者多、允許多種寫法、允許不同程度的人用自己的方式開發。

也因為社群龐大,PHP產出的程式碼平均品質低、開發者程度參差不齊。

那麼PHP到底解決了多少人的web問題呢?

關於PHP的缺點、批評,都是事實,但PHP社群知道自己在幹什麼。

正準備開始,不知去哪找人討論嗎?到批踢踢的PHP版、Facebook的PHP台灣發問吧!

學習了一陣子,覺得很多文章過時、想參考業界流行的開發慣例嗎?看看PHP: The Right Way吧!

對英文能力有自信,想跟全球的網友一起討論嗎?來Reddit的PHP版吧!

工作一段時間,想找些高品質的函式庫參考嗎?逛逛the PHP League吧!

好奇才華洋溢的PHP工程師到底有多少生產力嗎?看看symfony的Fabien Potencier的commits吧!

想自學寫網站、親手搞定某個web的問題嗎?試試看PHP吧!

軟體測試:我不在新創公司用mocks

為了確保軟體品質,多少會寫點測試。

常會看到文章提倡使用mocking的手法,說是要獨立測試程式的各個部份比較好。

我觀察這個手法很久了,遲遲不願意真正應用。

我總覺得mocking讓測試的code變得很冗長、不好讀。

最近終於發現,其實不少人跟我抱持同樣看法。

這邊舉Testing on the Toilet: Don’t Overuse Mocks一文中提到的例子。

不使用mocks、寫出有相依性的test長這樣:

public void testCreditCardIsCharged() {
  paymentProcessor = new PaymentProcessor(creditCardServer);
  paymentProcessor.processPayment(creditCard, Money.dollars(500));
  assertEquals(500, creditCardServer.getMostRecentCharge(creditCard));
}

使用mocking達到測試環境獨立的test長這樣:

public void testCreditCardIsCharged() {
  paymentProcessor = new PaymentProcessor(mockCreditCardServer);
  when(mockCreditCardServer.isServerAvailable()).thenReturn(true);
  when(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager);
  when(mockTransactionManager.getTransaction()).thenReturn(transaction);
  when(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment);
  when(mockPayment.isOverMaxBalance()).thenReturn(false);
  paymentProcessor.processPayment(creditCard, Money.dollars(500));
  verify(mockCreditCardServer).pay(transaction, creditCard, 500);
}

不但可讀性下降、還得多花時間撰寫mocks。

在新創公司,我不寫mocks的原因有兩個:

一、新創公司,軟體越快上線越好。花時間寫mocks,太不划算了。
二、既然軟體架構不大,反正任何一個測試fail,整個application都會壞掉。tests無論如何都得全部pass才行。

想來想去,只有在用到第三方library或API、不方便在tests中真正呼叫時,才會想用mocks。

話雖如此,有看法認為應該用interface再加上一層薄layer來解決test第三方library的問題:

Test Smell: Everything is mocked

如果是在開發framework就另當別論了。要確實分別測試各個部份才行。

Laravel的tests

話雖如此,也許更資深、接觸的軟體架構更大之後,我會改變想法也說不定。

工程師的缺德行為:叫朋友去學C/C++

常常看到非資訊背景的朋友問工程師:我最近想學寫程式!該怎麼入門?

通常他們會得到很多糟糕的答案:「先了解演算法」、「先弄懂資料結構」、「先認識物件導向」。

這些答案都夠糟糕了,但最糟糕的答案莫過於「從C/C++開始入門」。

(我看過最扯的建議是「去找C++ Primer Plus來看」。這本磚頭書絕對不適合一般人自學)

照著以上建議去做的朋友,大概會在兩週之內就得到滿滿的挫折感,然後認為自己「果然不適合寫程式」,也不好意思再去跟工程師講自己的狀況,最終再也不想踏入programming領域。

這些建議真是害人不淺。

資訊相關科系,是因為要接續到之後的資料結構、作業系統,所以大一從C/C++開始入門會比較連貫、才能完整學一次電腦的基本知識。

而這些想學coding的朋友,大部分只是這兩種情況:

A. 最近覺得寫網站好酷,想試試看自己能否寫個blog、或是個人網頁
B. 對工作上某些人工流程不滿意,想試試看自己能否學寫程式、用電腦解決問題

他們的願望僅此而已,並沒有打算成為電腦專家、駭客,實在沒有必要從C/C++入門。

就像點火一樣,先從零星的火苗開始、小心保護不要讓它熄滅,接著慢慢加東西進去、讓火焰慢慢成為大火。學習,除了知識/技能之外,培養成就感與熱情也是很重要的。而從C/C++入門,會讓正要萌芽的小火苗直接消滅殆盡。

我對程式設計入門的建議很簡單。
如果你是狀況A,去學PHP&MySQL,先弄一個醜醜網頁出來就可以了。
如果你是狀況B,去學Python,先弄一個能執行、能顯示幾個文字的程式就可以了。

一般人想到程式通常只想到「圖形化介面」,如果能寫出一個有介面的小玩意會非常有成就感。
那樣的話,學PHP來寫個網站、直接就是介面,絕對有趣;學Python很快就能把玩內建的tkinter函式庫、做出按鈕做出選單,樂趣無窮。

話雖如此,你到市面上找PHP或是Python的書,大概還是會落得「滿滿的挫折感然後正式放棄」的下場。

別擔心,這不是你的錯,是那些書寫得不夠好。沒有一本是真正的程式設計入門書。
那些書只想著給你知識/技能,沒有想到要培養你的成就感、保護你的熱情。

要找書的話,我只建議兩本。

狀況A,去找歐萊禮的「深入淺出 PHP 與 MySQL」(Head First PHP & MySQL)。
狀況B,去找歐萊禮的「深入淺出程式設計」(Head First Programming)。

已經出社會了嗎?去台灣大學資訊系統訓練班逛逛吧,看看時間跟課程費用能否負擔(我有經濟系背景的朋友去上過幾堂課程,學得滿開心)。

政府也有推動青年就業讚的課程,滿足某些條件還能申請補助(不過,我有朋友上過其中一堂PHP課程,印象很糟糕、覺得是業者要賺政府補助款隨便開設的課程。但畢竟是單一經驗,我還是列出來給各位參考。)

國外的線上開放課程也可以考慮。

Coursera的

人人都懂的編程課(Python)

學習編程:基礎

麻省理工的

Introduction to Computer Science and Programming

A Gentle Introduction to Programming Using Python

全部都是用Python入門。

我對程式設計入門的建議就只有這樣。你不需要知道什麼演算法、資料結構、物件導向。
什麼狗屁名詞都不需要知道。那大多只是工程師下意識地想讓你覺得他們很厲害而已。

你只要想辦法寫出一個會動的程式就可以了。所有偉大的東西都是這樣開始的。


Q1: 但是狀況A還要面對HTML/CSS耶.. 好像很難?
每個技能深入下去沒有不難的。但是HTML/CSS要入門實在不怎麼難。
就從隨便寫幾個表單或按鈕、弄個醜醜的靜態網頁出來開始吧。

Q2: 我是工程師,我能怎麼幫助朋友?
幫他們搞定環境設定吧!幫他們把最基礎卻又最給新手挫折感的環境給設定好!讓他們能用記事本跟幾個小工具就開始寫code!給他們成就感!

Q3: 那寫手機app呢?你怎麼沒提到?
我覺得手機app還滿難入門的,實在給不出有建設性的建議。不知道。不過,上面的連結也許有一些手機相關課程,不妨試試?

(Photo via Sano Rin, CC licensed.)

我對Rails之父的觀察

Rails之父,網路代號DHH,本名David Heinemeier Hansson。
我不認識他本人,但是從他自己的blog、他的合夥人Jason Fried的描述、媒體的採訪,我發現幾件事和想像中的不一樣。撰文和各位分享。


他本來是個有商業天賦的PHP工程師

DHH的合夥人Jason Fried在Hacker News親自寫道

我遇見DHH時他是個PHP工程師。我寫過一些PHP,能夠稍微評估他的能力。除此之外,我還喜歡他的商業素養以及平常做事的方法—都跟我很合。(*1)

他本來不覺得自己適合當程式設計師

他接受Big Think訪問時,其中這段提到:

說來有點好笑。我以前寫PHP跟Java的時候,常常花時間去摸其他程式語言。到處摸看看其他程式語言…隨便什麼都好。寫PHP跟Java實在太悶了,我需要用這種方式讓自己暫時抽離。(*2)

還有這段:

我以前寫PHP跟Java的時候,完全不覺得自己之後會當程式設計師。(*3)

閱讀程式設計的教材、範本給他很多苦悶,他無法那樣學習

他在網誌中透漏,自己沒有對電腦熱愛到能夠純粹為了學習而學習。
請參閱我之前的翻譯:Rails之父:我怎麼學會寫程式的?

他不是資工系畢業的

看他在signalvnoise部落格下方的評論,網友Ben提到:

@DHH
你沒有資訊工程學位。這說明了超多事情。

DHH在下面回應:

我其實有半個資工學位(我們系是一半商業管理、一半電腦科學)。但我沒在學校真的學到怎麼寫程式。(*4)

翻閱維基百科的介紹

從Copenhagen商學院拿到商管與電腦科學學位後,他從丹麥搬到美國。(*5)

他拿的學位等於台灣的「資訊管理學系」。

他一開始只是接案,不是決心創業

DHH以Basecamp(以前叫37signals)公司合夥人的身份跟Jason Fried合出過幾本書。
大家都知道Rails是開發Basecamp的過程中,從code中抽離出來而成的框架。
那麼一開始他是如何定位自己呢?
我寫信給Jason Fried本人,問他股權的事情。他的答覆如下:

Hi Tony-

我們最一開始沒有談股權的事。

我一開始是發案給他,之後僱用成員工。我們一起工作了好幾年才談到公司所有權的事。

-Jason(*6)


*1
I’ve done some PHP programming in the past, and I met DHH when he was a PHP programmer, so I was able to evaluate his talent at a very basic level. Beyond that, however, I liked his business mind and general approach to things – they were closely aligned with my own.

*2
It’s kind of funny; when I used to develop in PHP or the stuff I did in Java, I was always looking for something else. I was always looking for another programming language, another… just something else, in part just to distract me from being bored in the languages I was in.

*3
I was absolutely not convinced that I was going to be a programmer when I was working with PHP and Java.

*4
I actually got half a comp sci degree (my bachelor was in biz admin/comp sci), but that wasn’t where I truly learned to program.

*5
After graduating from the Copenhagen Business School and receiving his bachelor’s degree in Computer Science and Business Administration, he moved from Denmark to Chicago, Illinois, U.S. in November 2005.

*6
Hi Tony-

We didn’t talk about stock at the very beginning.

I hired David as a contractor first, then an employee. We worked
together for a few years before there was any talk about ownership.

-Jason

軟體架構:Domain Driven Design

從事軟體開發,特別是從Web領域開始的developers,一定因為各框架普遍提供MVC架構的關係而大致了解MVC的功能區分。

然而,當專案大了起來,很快就會發現如何讓MVC分工,比想像中困難:

* 某些code到底該放在C還是M?

* 修改V內的某些code會直接影響到business logic的運作與安全…這不應該吧?

很顯然地,對軟體架構的知識只擁有MVC是不夠的。

我舉一個更殘酷的例子。我在Reddit曾經和國外的工程師討論MVC的問題:

What exactly is ‘model’ in MVC?

最高分的回答如下,謝謝Nephand

I would say think of the ‘model’ more as your application – it’s not a single thing. The ‘view’ and ‘controller’ are just a channel of communication into your application’s behaviours.
Another word to replace ‘model’ might be ‘domain’ – see Domain Driven Design. Your application domain might need to deal with emails, or SMS but such things aren’t specific to your domain – generally speaking – so they’re services that your model is aware of (even if it’s just dispatching an event that your services listen for). They’re channels of communication into and out of your domain.
Your web interface talks to the application domain through a view and controller. Yet imagine you want to offer an API – this has no need of the View [simplistically put]. Also, your application might talk to the outside world through an SMS Service, or an Email Service and so on.
Personally, I like Hexagonal architecture as a way of conceptualising this.
Your ‘model’/’domain’/’core application’ then might have, for example, a persistence/database layer which creates a ‘seam’ between your application’s logic and the actual data storage format. This can be great, it translates out complex objects into a simple data format (SQL Rows…Mongo Documents…Redis)
You mention the idea of ‘one model’ and ‘one table’ – and you’re right to say this part is confusing. The ‘model == table’ approach has plenty of benefits, in that it’s pretty simple and easy to follow – but it can be limiting.
Having a ‘seam’ between your model and the data store becomes useful as it grows more complex. Within your ‘model’ you might have one ‘god object’ that serves as a root for multiple sub-objects. Each part can encapsulate behaviours that its containing object doesn’t need to know about – but all of that complexity still constitutes just one table row.
Domain Driven Design (as a primary voice but not the only one) differentiates Entity and Value Object [the classic Value Object being Money ].
The Entity might represent what you’re thinking of as a ‘row’ in your table (in the most general sense) – it has something that makes you want to be able to track its lifespan and differentiate it from others inside your application (e.g. a unique primary key ‘ID’).
Quick and dirty example: A User. A User would/could be an Entity, with a unique identifier. A User might have an email address. The concept of an ‘Email Address’ is not unique to a User, and so it might be a good candidate for being a Value Object that can be used by anything in your application domain/model. So when you later need to give another Entity (say a Company) an email address, you can re-use that same value object and all of the behaviours you might want for all EmailAddresses in your domain.
Lets say your business requires a hatred of hotmail users, and will never consider @hotmail.com a valid email address. You can then encapsulate such a rule into your EmailAddress object’s constructor [other approaches are preferred perhaps but I did say quick and dirty].
Whether you use EmailAddress in the context of a User or a Company, it will always throw an Exception / fail elegantly.
This is not [typically] intended to validate user input, but to ensure that your model/domain is meeting your hotmail hatred requirement by not allowing an EmailAddress value object for “*@hotmail.com” to be created. Whether that is passed in from a web form, over an API, or parsed in from an SMS interface shouldn’t matter to your core logic – you’re just saying ‘never allow this to happen even if it makes it past input validation somehow’.
This comes back to your concern about having a complex model separated into different models and in turn tables. That is where different strategies for mapping your ‘model’ to your data store come in. The Data Mapper is arguably the most flexible – and in turn potentially complicated – to implement. Patterns of Enterprise Application Architecture in combination with the Evans book Domain Driven Design can go a long way to help you understand these ideas, even if you water them down to ‘keep it simple [stupid]’.
Basically your User table (VALUES user_id, email_address) would be updated with the $user->id and $user->emailAddress->getAddress() values to store. A Company would look similar, $company->id, $company->emailAddress->getAddress().
The great part about all of this is that you can start to break up and refactor your god objects into smaller parts, that can be persisted separately or not. While your god object might still serve as a ‘root’ object, you will hopefully have been able to split its behaviours off into smaller chunks that can be tested easily, persisted easily, and even re-used where needed by other parts of your model.
This is all well and good, but don’t over-engineer things. If it’s just a string value, then just let it be a string value. Until you arrive at a behaviour requirement that makes it useful to implement an abstraction then it’s probably just bloat.
Obligatory warning This is a massive over-simplification of some more interesting concepts than I’d be able to explain – both due to lack of experience and time. Apologies to the knowledgeable if it’s a bad explanation – and to everyone else if it’s especially rambling, I don’t have time to proof read.
Quick Links for getting to grips with the model: – Unbreakable Domain Models – good quick intro. – Domain Driven Design – Patterns of Enterprise Application Architecture – Command Query Request Separation

為了回答我關於MVC的問題,他引用了大量的專有名詞…我完全看不懂,但大致知道是關於Domain Driven Design的東西。看起來這些概念已經是國外討論軟體架構的必備知識了。

而這些,在台灣很少聽人提到。


關於DDD其實我早有耳聞。Rails之父DHH在

The five programming books that meant most to me

曾經提到一本關於Domain Driven Design的書:由Eric Evans撰寫的Domain-Driven Design: Tackling Complexity in the Heart of Software

我翻過之後實在是..很厚重,再加上又是原文,唸不下去。

今天在網路上找到此書由其他作家精簡過之後,接著翻譯為簡體中文的版本

领域驱动设计精简版

我還沒有心得可以分享,因為我還沒有消化完。

但是目前翻閱了三分之一,確實非常精彩。

開卷有益啊朋友,有空翻一翻吧。

軟體開發:你不會用到

最近在從事軟體開發時,常有兩種荒謬的感覺。

情境一


為了滿足新的需求,我需要寫一個類別。
總覺得可以多寫幾個函式,創造出某種漂亮的架構…算了,我先寫幾個蠢函式來用好了。
(動手寫code)…。
咦?突然就結束了。已經成功了,這幾個蠢函式已經滿足所有需求了。
不想進一步去改寫成什麼漂亮架構了。

真荒謬,我是不是只做了半成品就交差?

情境二


為了滿足新的需求,我需要寫一個類別。
總覺得可以多寫幾個函式,創造出某種漂亮的架構…算了,我先寫幾個蠢函式來用好了。
(動手寫code)…。
啊,解決一部份問題了,不過這幾個蠢函式果然不夠用。
只好把這幾個蠢函式全部刪掉重寫了。
(動手寫code)…。
多寫幾個函式,創造出漂亮的架構了。解決問題了!
我是不是一開始不應該寫那幾個蠢函式啊?
明明感覺不太夠用卻還繼續寫,果然全部刪掉重寫了。浪費了一些時間。

真荒謬,每次都硬從蠢函式開始寫。根本是刻意在浪費時間?

思考這個問題的時候,找到了一篇相關的文章。
翻譯出來跟各位分享。
原文:You Arent Gonna Need It


你不會用到

「你不會用到」是極限編程的一種實踐:

「永遠在你真正需要的時候才去實作,別預估需要就去實作。」

即使你非常、非常、非常確定你等一下就會需要這個功能,也不要現在就實作。事情通常有兩種發展:
一、你發現根本不需要
二、你發現真正需要的跟一開始預估的差很多

這不是在說軟體的彈性不重要。這是在說,你不應該根據你以為晚點需要的東西去過度設計。

這種實踐有兩個主要理由:

* 節省時間。因為你沒去寫那些最後發現並不需要的程式碼。
* 讓程式碼更漂亮。因為你沒讓一開始那多少有點弄錯的預估去汙染你的程式碼。

RonJeffries提出一個情境說明:
你正在寫某個類別。你剛加上幾個你需要的功能。你知道你晚點會需要其他幾個功能。如果你現在不需要,就不要現在加進去。
為什麼?

「好吧,Sam,你幹嘛現在加上去?」
「呃,Ron,這會讓我們節省一些時間啊。」

除非你我活在平行宇宙,不然你無法現在多做事情,卻在「節省」時間。除非晚點做這件事比現在做更花時間。所以你的意思是:

「如果現在多花點功夫做好它,最後總共花的時間會比較少。」

除非你我的專案長不一樣,不然專案都是那副德性:現在要做的事情總是有夠多。在事情很多的時候還想做更多,是非常糟糕的。

然後除非你我的腦袋構造長不一樣,不然很有可能你最後會發現根本用不著,或是你用到的時候必須重寫、修正這些程式碼。不管哪個發生了,總體來看你都是在浪費時間,還導致你到時無法把真正需要的東西加進去。

「可是,Ron,我現在知道怎麼做,晚點我可能會忘記。」
「所以,Sam,你是在說你寫的類別複雜到你本人也無法維護?」

保持簡單。如果你需要,晚點再加上去即可。如果你不需要,根本別費這功夫了。今天就好好休息吧。

(Photo via Héctor García, CC licensed.)