在2013即將結束的最后一個月里,我跑客戶的時間時間達到了26天,作為一個技術出身的我這是非常不可思議的,在多年前我敢都不敢想! 在歷史上一個月里我連續工作的天數也就27天,當然這是呆在公司辦公室里,負責碼代碼,不會與直接客戶面對面接觸的(目前大多數技術人員都是如此)。這段時間的持續改變讓我不得不深思!
一. 代碼的價值
案例1:
最近有個項目實施難度有點大,當然我本身是技術出身,所以有時候還是比較有優勢,因為可以當場為客戶解決問題。我們是為一家生產型企業實施一個生產倉庫管理系統,現在條件比較復雜,反正就是涉及到生產,出入庫等!實施到一個出庫的時候,有個功能是這樣的:
客戶要求公司客服人員直接查詢倉庫庫存,然后根據訂單配貨,這個配貨是可以合並多個訂單的,做好配貨單要系統生成一個揀貨清單[也就是告訴倉庫人員到哪里去拿貨,在貨架哪里,在貨架第幾層,是否需要拆托盤,是否要拆箱等等],倉庫人員拿着清單直接到倉庫將取貨讓后用叉車搬到代發貨區域。然后發貨人員拿着PDA(非常老式的那種)去掃描箱碼或者托盤條碼進行配貨校驗,最后打印發運標簽(每個箱子上都要貼這種標簽,每個單子估計都有幾百上千箱貨)。
現在有個問題就是這搬出來的貨要自動分配到不同的訂單中去,而且要滿足訂單情況。
比如A訂單 產品P 需要數量 16,000 ; B 訂單需要產品 P 數量為 54,000; C訂單需要產品P數量 16,000; C 訂單需要產品P1 2,000;
倉庫揀貨數量為: 16,000+54,000+16,000 (P產品) ; 2,000 (P1產品)
P產品包裝: 標准包裝5,000 P1產品包裝: 標准包裝 600
實際揀貨為: 4*5000(整箱) + 10*5000(整箱)+ 2*5000(整箱) + 2000(零箱)+ 4000(零箱) --P產品; 3*600(整箱)+200(零箱)
那上面分配貨品有很多種分配方式,不同的訂單可以共箱(在一個箱子上貼兩個標簽 200 + 400 分別是不同的訂單)。
至於具體情況如何,這個也不是一會能夠描述清楚的,做過揀貨的朋友應該清楚:
問題是我寫了一段代碼,自動分配揀貨的,大概花了我4天左右的時間,總共代碼行估計接近一千行了,代碼部分如下:

int qty = detail.Qty; int fullBoxNum = packageNum != 0 ? qty / packageNum : 0; //理論整箱箱數 int sinQty = packageNum != 0 ? qty % packageNum : 0; //理論零箱數 int sinBoxNum = sinQty == 0 ? 0 : 1; //理論零箱箱數 string PO = detail.OrderNum;//PO ShipLableEntity ship = null; /** * 當揀貨得到的箱子中整箱數大於0的情況: * 當揀貨得到的箱子中整箱數==0的情況: 說明所有的揀貨都要從零箱中去組合得到 * */ if (listFullBoxes.Count > 0) { //當理論揀貨整箱箱數大於0 且 實際整箱也大於0的時候 if (fullBoxNum > 0) { //如果理論總箱數小於或等於實際總箱數則扣除實際理論箱數,並且判斷是否有零箱 if (fullBoxNum <= listFullBoxes.Count) { ship = FormatShipLable(product, PO, outNum, listFullBoxes.First(), orderEntity, index, fullBoxNum, listDefault, listBills); listShipLables.Add(ship); listFullBoxes.RemoveRange(0, fullBoxNum); index++; //判斷有零箱的情況,找到最適合的箱數,沒有就要拆箱分組. 如果沒有零箱什么都不用處理 所以不需要else if (sinBoxNum == 1) { //如果存在理論零箱的時候,先要實際零箱的箱數是否大於0且零箱的總數也大於理論零箱的數量 if (listSinBoxes.Count > 0 && listSinBoxes.Sum(a=>a.QTY)>=sinQty) { //升序排列,查找大於等於零箱數的第一個箱拆分 listSinBoxes = listSinBoxes.OrderBy(a => a.QTY).ToList(); OutBillEntity tempBill = listSinBoxes.Where(a => a.QTY >= sinQty).FirstOrDefault(); //如果存在滿足上面條件的箱,則需要拆分處理 if (tempBill != null) { //如果數量剛好滿足則直接生成標簽 如果不滿足則拆分為兩個 if (tempBill.QTY == sinQty) { ship = FormatShipLable(product, PO, outNum, tempBill, orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); listSinBoxes.Remove(tempBill); index++; } else { tempBill.QTY = tempBill.QTY - sinQty; OutBillEntity billNew = new OutBillEntity(); billNew.LairdPN = tempBill.LairdPN; billNew.CustomerPN = tempBill.CustomerPN; billNew.StackBarCode = tempBill.StackBarCode; billNew.BoxBarCode = tempBill.BoxBarCode; billNew.QTY = sinQty; ship = FormatShipLable(product, PO, outNum, billNew, orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); if (tempBill.QTY <= 0) { listSinBoxes.Remove(tempBill); } index++; } } else { //如果不存在則需要組合得到,查詢組合求和數量小於等於零箱的數量 int zuQty = 0; //計算總和 int sinIndex = 0; //滿足條件之后停止的索引 for (int i = 0; i < listSinBoxes.Count; i++) { zuQty += listSinBoxes[i].QTY; if (zuQty >= sinQty) { sinIndex = i; break; } } //如果剛好滿足則生成相應的標簽,並且刪除 if (zuQty == sinQty) { for (int i = 0; i <= sinIndex; i++) { ship = FormatShipLable(product, PO, outNum, listSinBoxes[i], orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); index++; } listSinBoxes.RemoveRange(0, sinIndex + 1); } else { //刪除之前的,最后一個拆分 int beforeQty = 0; for (int i = 0; i <= sinIndex - 1; i++) { beforeQty += listSinBoxes[i].QTY; ship = FormatShipLable(product, PO, outNum, listSinBoxes[i], orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); index++; } tempBill = listSinBoxes[sinIndex]; tempBill.QTY = tempBill.QTY - (sinQty - beforeQty); OutBillEntity billNew = new OutBillEntity(); billNew.LairdPN = tempBill.LairdPN; billNew.CustomerPN = tempBill.CustomerPN; billNew.StackBarCode = tempBill.StackBarCode; billNew.BoxBarCode = tempBill.BoxBarCode; billNew.QTY = sinQty - beforeQty; ship = FormatShipLable(product, PO, outNum, billNew, orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); index++; listSinBoxes.RemoveRange(0, sinIndex); } } } else { //如果實際零箱數為0 或者實際零箱數小於理論零箱數,那么就要拆整箱,因為實際整箱大於理論整箱所以拆整箱 if (listFullBoxes.Count > 0) { OutBillEntity tempBill = listFullBoxes.First(); tempBill.QTY = tempBill.QTY - sinQty; listFullBoxes.Remove(tempBill); listSinBoxes.Add(tempBill); OutBillEntity billNew = new OutBillEntity(); billNew.LairdPN = tempBill.LairdPN; billNew.CustomerPN = tempBill.CustomerPN; billNew.StackBarCode = tempBill.StackBarCode; billNew.BoxBarCode = tempBill.BoxBarCode; billNew.QTY = sinQty; ship = FormatShipLable(product, PO, outNum, billNew, orderEntity, index, 1, listDefault, listBills); listShipLables.Add(ship); index++; } } } }
以上代碼可以忽略不計,跑到客戶那邊實施這個功能(部分上線) , 當時我感覺很牛逼,因為我作為實施人員還在寫代碼 而且還搞定了,公司的其他技術人員沒有搞定。 到客戶現場使用之后,工廠工人說這個太慢了不好用,給他們領導反饋這個他們不會使用這個東西的,完全是浪費他們時間,上報到公司之后后果各位也想得出來,被客戶噼里啪啦的說了一頓說做的東西不行怎么怎么。
心理受到極大的創傷啊,自己辛辛苦苦弄出來的東西就這樣一句話給抹殺了,實在是痛心。

OPEN MyCursor FETCH NEXT FROM MyCursor INTO @RowNum,@Qty WHILE @@FETCH_STATUS=0 BEGIN set @SumQty = @SumQty+@Qty IF @SumQty>=@Num + (@PackageQty * 5) BEGIN break END FETCH NEXT FROM MyCursor INTO @RowNum,@Qty END CLOSE MyCursor DEALLOCATE MyCursor--釋放游標 ; WITH TempTable AS ( SELECT ROW_NUMBER() OVER (ORDER BY DCTime ASC,ID DESC) RowNumber,DCTime,ID, OrderNum,CusNum,CreateTime,LairdPN,CustomerPN,Qty,LocalNum,StackBarCode,BoxBarCode,HasStack,IsBlend,IsHalfBox,LairdNum,LairdBoxNum,IsFullStack FROM LocalProductRel WHERE LairdPN=@LairdPN AND CustomerPN=@CustomerPN AND IsLock=2 AND IsReject=2 AND IsRework=2 AND IsChange=2 AND IsOnlyCustomer=2 AND LocalNum!='XF9-9-9' AND [StorageNum]='7112' AND Qty=@PackageQty ) SELECT * FROM TempTable WHERE RowNumber<=@RowNum
回去之后又努力優化程序,大概又花了兩天時間,速度提升了不少,再去客戶那邊操作人員滿意OK,得到贊賞,心里美滋滋啊!
倉庫操作人員其實不懂計算機的,只會上QQ,看新聞什么的,編程什么的就是妄想之中的事情。所以你寫的代碼好與壞跟他們無關。上面的代碼開始我認為自己寫的很好,可事實不是這樣的,客戶不買你的帳,你寫的再好客戶又看不懂對他們來說毫無意義,一不會給他們減輕工足量反而增大了,二不能給他們美感(軟件上沒有美女),三不會給他加工資。
第一段代碼你寫的再多,你花了多少天時間,其實在客戶面前是毫無價值的。
第二段代碼多么的丑陋,好多問題(我自己認為,當時為了解決問題沒有考慮那么多的代碼有雅性),但是對於客戶來說非常有價值減輕了他們的工足量。
總結如下:
(1) 代碼量的多少以及編寫代碼所花費的時間都不是軟件的價值
(2) 寫代碼最重要的目的是解決實際問題(操作方便,減輕工作,舒適美感)
(3) 代碼對軟件的價值是體現軟件的便捷性以及強大的功能
(4) 客戶是不會買你代碼量的賬的,客戶不懂代碼也不願意鳥你的代碼是如何實現的
(5) 解決客戶問題了你的代碼就有價值
以上結論不代表說代碼規范性不重要,代碼規范性是為了實現好的軟件,將軟件的優勢體現出來,代碼本身是不具備任何價值的。
二. 服務價值和軟件價值
軟件是為客戶服務的,這兩者有必然的聯系! 那軟件的價值是不是就是服務的價值呢。
問題1:客戶是買你的軟件還是買你的服務
問題2: 除軟件之外的服務,你的價值又體現在哪里
現在很多公司都很牛氣,給某個公司賣了一套軟件然后實施成功之后,那后面的技術服務態度就爛的.... ,以前深有體會。他們總會這樣回復:" 我們的軟件在其他公司都用的很好,就你們的問題特別多,你們現在的問題不屬於我們的服務范圍 "。當初買軟件的時候到底是買的軟件還是服務啊,反正現在也說不清了。現在個人認為客戶買你的軟件是需要你的軟件服務他們公司更好的協同工作管理,而所需的服務就是在軟件出現問題的時候或者軟件不知道怎么使用的時候能夠快速提供幫助。所謂服務其實也就是"客戶哪里癢你就幫他撓哪里"。
同樣一款軟件很多公司都可以開發,而且開發的很不錯,無論是在操作便捷性還是界面美觀性上都很不錯,現在這樣的公司非常多。但是客戶為什么要選擇你為他們開發軟件,難倒真的是因為你們公司其他公司技術要厲害么(這里排除價格的問題)。
這里先給各位看看一個界面的截圖:
這個是打印一個施工單的部分界面截圖,這個頁面打印可能稍微有點復雜啊,不過我相信大部分技術公司都能夠做出來.
當時我公司去實施這個項目的時候,並沒有提到這個東西,打印一個施工單據隨着生產施工去流轉。因為在生產車間不能使用電腦,某個產品生產到那個步驟了需要知道。我們提出了一個方案就是將每一次施工的流程打印出來放到車間中去,每到工藝流程結束之后這個施工單會隨着產品走動,操作人員可以看到目前進行到哪一步了。
這個功能並不是很強大,功能也不是很起眼,但是這個很重要,為什么,因為你幫助他們實際解決了生產車間的問題,生產車間的環境你以為都跟辦公室一樣啊。別說工人賺的錢比你碼農多,他們的的確確是辛苦錢。
在這個問題上,我們除了客戶要求的軟件功能之外,還幫他們處理一件實際操作中的問題,客戶沒有想到用這種方式來解決,但是我們想到了並且幫他們實施了,我們的價值體現出來了。我們的價值就是解決客戶實際生產中遇到的問題,我們是深入到他們的生產工藝流程中去了,而不只是簡單的為他們開發了一套軟件然后他們可以用。 很多公司認為我們為客戶開發了一套軟件這就是我們的價值,這話的確沒錯,但是你能夠做到的其他公司也能夠做到,但是都沒有解決客戶痛癢的根源。
總結:
(1) 軟件的開發只能體現你的技術能力比較強
(2) 能夠解決客戶的痛癢問題那就是你的真正價值,這個部分在軟件之內但是絕對的高於軟件
(3) 客戶對你的認可肯定不只是你會開發軟件
(4) 服務不只是隨叫隨到,而是主動去幫助思考問題
三. 個人的價值
跑了好多工廠,也認識了很多很多工廠車間工作的工人,有時候我們會討論到工資的問題,估算下來他們的平均工資也就3000-4000左右吧,這算比較不錯的了(這里是只流水線上的工人,不是車間里面的技術工,技術工也有很高工資的,甚至比碼農還要高)。目前我所了解到的做開發的技術人員工資畢業差不多也有四五千吧,我公司是這樣的一個標准。
工廠流水工工資級別停留在了3000-4000,而技術開發人員這只是一個起步,不同的行業為什么有這么大的差別?
顯而易見的是技術開發人員是腦力工作,而流水工時體力工作,這個社會大的基本形態就是這樣的,腦力比體力賺錢多。
下面的觀點請各位勿噴:
1. 外包的為什么總是抱怨自己學不到公司,在我的觀點中外包就是碼代碼的,這屬於腦力中的體力活,幾乎很少有人會去管這個事情是不是一定要干好,你的價值就是代碼量的產出量
2. 為什么公司總是不給我加薪,或者加薪那么少? 作為程序員你為公司是奉獻了腦力還是體力?如果程序員能夠很清楚這個我估計加薪也不會少?[之前有給公司提過50%的加薪,公司很爽快答應了,當然也有進步源於起點低的原因]
3. 我不只是碼代碼的程序員,很多人都是這么想但是很多人都是這么做的,到公司你就將自己定位了自己是做技術的,什么是做技術那就是寫代碼。
4. 如何發揮自己的價值,不是寫好代碼就可以了,還要讓自己寫好的代碼轉化為讓客戶接受並欣賞,那就是你的價值。
5. 你做一件事如果你發現今后有讓你激動的地方那你就有價值了。
以前的一個同事給我說,公司那一年只有你們項目做的東西在公司算是真正盈利了的,想當初那是何等的痛苦啊,當時自己都瞧不起那樣的項目,可是客戶認可產生了價值,你也就有價值了,當時你解決了公司的項目問題並且讓其得以持續下去(解決問題不一定只是技術問題)。聽到這個熱淚盈眶啊,你所有付出的都得到了認可,你是有價值的人。
本文也是雜七雜八的亂寫,貌似現在做了實施事情時間也比較自由多了,關鍵是還有時間寫文章啊,雖然寫的不怎么好,希望大家勿噴!
換一種思路換一種角度思考問題可能會讓你更加的透徹明白,最近自己的一些工作體會分享給還在辦公室角落獨自寫代碼的技術人員,走出來看看或許會讓你的目標更加明朗!