2012年1月21日 星期六

商業操弄,軟體研發人員不喜歡做的事!

Standard
在國外,許多大型軟體公司林立,老牌軟體商建立了不少現今軟體業仍在使用的遊戲規則。而比較小的軟體外包商、工作室或是個人,也糊裡糊塗依照這些大公司使用的遊戲規則走,甚至是遵守不合時宜的淺規則。然後,軟體業進入了國內,又被代工思維牽著走,軟體業因此哀嚎聲不斷。軟體研發人才不喜歡去操弄商業,卻被商業操弄著。

身為軟體外包或是軟體客製化的外包商,你是否曾遇過,客戶有很多大大小小的鎖碎要求,不定期會交代過來? 這些修修改改,看似不癢的功能和需求,做起來毫不費功夫,三兩下順手就能完成,卻往往造成雙方驗收標地的分岐,此外,不知不覺中所累積的份量,也讓結案時間遙遙無期,甚至造成軟體產生更多的問題待解決。對於只想當水電工的研發人員,總是在客戶的要求下,修完馬桶順道通水管,又補個土、也打個臘。另外,如果馬桶旁邊剛好有漏水,那你要自認倒楣了,因為客戶也會請你幫忙『順手』解決一下,或認定是你在修馬桶時弄壞的。是吧,令人難過。

難道研發人員就要這麼可憐嗎?我們做錯了什麼,為何就要這樣被對待?明明是為了提供更好服務,卻累死三軍,又讓人有把柄指著鼻子唸。更慘的是,一旦這些看似不起眼的修修改改,累積成有影響力,讓專案與合約內的需求不完全相符。客戶就有機會利用『與合約不符』的矛盾點,再殺下一城。

筆者也同是天涯淪落人,曾經不斷思考這問題也同時咒罵著台灣的業界,咒罵他們總不給軟體商有活命的空間。尤其受了委屈後,更是一股惱自命不凡,認為自己無論做什麼樣的事,都應該比一般人收取更多費用,以平心頭之恨。但過些時日後,經過冷靜思考,發現是一開始雙方的互動模式就錯了,或許更仔細檢視這個行業,才能找到改變現況的辦法。

大多數軟體開發不是演藝事業

一直不認為『軟體程式設計師』是演藝事業,所謂『台上一分鐘,台下十年功』不能完全形容軟體開發的工作。雖然,越厲害的研發人員,越能設計好品質的軟體;越是名不經傳的軟體商,也越難生存於業界。許多面向與藝人生態有些相似,但其實完全不是這個樣子。

因為我們雖練功多年,在專案過程中,卻仍然做很多沒技術性又取代性高的工作,或是『順手工作』這類連項目都寫不出來的事。所以,與演藝事業最大的不同是,我們不是只做一件事:『像巨星一樣站上台吸引全場目光,解決觀眾們長達數十分鐘的無聊時間』,而是因應專案需求,巨星仍然要下海當工讀生或工作人員,甚至是清潔工。所以,大多數軟體研發人員,其實扮演的是『跨年晚會工程的承包商』,不是巨星。許多客戶的額外零碎需求,就像有人在會場中亂扔垃圾需要被收拾,或是突然有人身體不適昏倒需要救護車送醫,這些都無法預期也無法很容易的按工計酬。

這一切,除非你有將風險成本都算在專案內,更重要的是案子也要夠大,不然相當不容易去完成工作。這也是為什麼許多比較大的軟體商,比較不受影響,因為他們接的案子也夠大,風險成本都能很輕易的被安插進總報價裡面。

因此,除了這類大型軟體商,多數中小外包商和客戶的想法,其實一開始便有了分岐。很多研發人員真的分不太清楚,自己只是當個明星,還是承包整個工程的承包商。但無論如何,客戶的心中一直很清楚認定:『承接專案的人就是整場晚會的承包商,我們不管你是不是把巨星拿來當小弟用,我們要得到的就是一個成功又圓滿的晚會』。

一切的痛苦,已經不能責怪是不是當初定需求不夠明確,也不能怪研發人員利用太多『舉手之勞』提供給客戶額外的服務。最後你會發現,想再多的方法與客戶確認需求、利用商業手段與客戶定下契約,或是出動威脅利誘的業務手法與客戶協商,都是頭痛醫頭腳痛醫腳之舉。或許先認清楚自己想要做的角色和可以做的角色,再引入適當的制度,才是突破現況的第一要務。


我們只想當水電工

說明白了,我們只想當個『軟體水電工』,有工就有酬,沒經驗的小工拿一份酬,有經驗的工頭拿雙倍酬,立即解決問題就立即收錢,然後打道回府等下一個工。至於整體工程承包,我們其實一點興趣都沒有。

只是,當水電工似乎是遙不可及的夢,因為客戶就是自己不能解決或決定,才外包找人解決,他們想要得到的是整個解決方案,而不只是工人。更重要的是,在資訊科技業中,客戶無法看到漏水就能自行判斷要找水電工,而是要先找醫生診斷病情,然後才選擇治療方案和醫療團隊。

雖然很可惜的是,大多數情況下,軟體研發人員沒辦法只當個『水電工』,但肯定有不少需求,是可以規劃成專業服務,使用水電工的收費方式,若是多加思考,應該能有些斬穫。



我們也許可以當醫生

其實仔細省閱一下醫生的工作,除了要聽取問題、診斷病情、評估可行性和提出處方,必要時要進入手術室開刀和操作儀器去治療病人,就像軟體外包在做的一般,開玩笑的說,我們也許可以稱醫生為『人體工程師』。此外,而客戶基於安心考量,多少也會自行去尋找名醫。這些種種情況,個人認為軟體研發人員其實更像個醫生。

因為像醫生,所以在簽約進行手術或治療前,我們也必需要做許多評估。但不一樣的是,無論醫生是否有治療成功,醫院總是有錢領,但軟體業則是要摸著鼻子認了。許多軟體外包商,在談案子的階段,最怕的就是不清楚技術上可行與否,或是不知自己有沒有能力達成。所以,多數軟體研發人員在面對不同案子簽約之前,就算不確定是否會得到案子,也都會先做許多評估或提前研究,甚至,必需提前偷跑才能知道可行性。

不過,面對現在科技產業客戶都追求創新的情勢,這種不確定性的程度更甚也更常見,如果案子接到了還算好,如果沒接到,這種事前研究期的資源浪費,讓更多中小軟體商非常痛苦。更還沒提到,接到案子後如果有不可預期的技術瓶頸(像是配合的硬體平台有嚴重且之前未被發現的 Bug),造成手術失敗或時間拉長的情況。真正的醫療體系或許還有保險可以支付手術費用,但在軟體業,這意味著無法結案,收不到款。

因為這樣不易收到款的制度,軟體承包商為了結案,屢見不鮮地用盡『各種手段』處理和討好客戶的需求,包括了文章開頭提到的『舉手之勞』和隨之而來的各種問題。導致其開發出來的軟體品質不佳,客戶也得不到最好的成果,常有狗尾續貂的情況發生,講的更難聽些,虎頭蛇尾會更為貼切。

可憐的是,軟體研發不是人命關天,一切在商言商,客戶們通常不願只付『掛號費』,還可能貨比三家。哪一家能『價格殺的夠低』又拍胸脯『保證全部完成』,哪一家就能搶到案子。如此惡性競爭,更加深擴大了軟體研發人員被劃開的傷口。

軟體產業沒有軟體健康保險補助,不是所有人都能夠負擔起看醫生、身體檢查和評估的費用,所以在這產業中有錢人才能夠看醫生。不然就是醫生得扛起所有風險和責任,並吸收許多成本,才能去拓展市場。

或許,要求政府有相應的保險制度不太可能,但更細的分工整合和處方制度,可能是一個可行的思路。


試圖尋求保障雙方的軟體開發模式

客戶和軟體商雙方的拉距戰,就像一個很大的槓桿,一不小心另一邊就飛上了雲端。

我們可以理解,不能完全怪罪於客戶,畢竟所有人都總是希望得到完整的解決方案、降低風險,又盡可能支付最少的錢。小客戶是預算不高要緊扣,大客戶則是怕付了錢,卻達不到目的,或是得到一堆無法使用的 01 文字。

但對於軟體研發人員來說,卻也太過不公平,只能全賠和拼命乞討二選一。

現有的各種手段都無法幫助軟體商突破現況,太多的慣例也惡習累積已久。這也是為什麼,很多以接案起家的新創公司(Startup),到五、六個人之後,開始碰到瓶頸。因為小案子已經養不起公司,中間的案子不好做,再上去就要一股作氣引進人員到數十人才能做。但人數到這麼多時,大案子的獲利也只是勉勉強強。

所以,如何想出方法,雙方保障,是不可放棄的目標。


後記

本文只是先起了個開頭,省視當前情勢。雖然對於實際執行,已經有些初步方案,有空再記錄下來,期望大家共同尋找出一個可行的制度。

2012年1月11日 星期三

NodeJS + Express + i18next 支援多國語系吧!

Standard
除非是區域性的網站服務,不然在這個網路通全世界的時代,開發網站服務就一定有多國語系的需求。一般來說,i18n 的支援都是由 Web Framework 所提供,但 Express 並沒有支援,所以我們要借助 i18next 這個模組。i18next 可以和 Express 以及 template 很完美的結合,最重要的是,有客戶端支援(clientside support),若搭配 jquery,我們也可以使前端的 JavaScript 支援多國語系。

先使用 NPM 安裝 i18next:
npm install i18next

在 express 的應用程式(app.js)中引入使用 i18next:
var express = require('express');
var i18n = require('i18next');

var app = module.exports = express.createServer();

/* Initializing i18n */
i18n.init();
i18n.registerAppHelper(app);

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(i18n.handle);
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.get('/', function(req, res) {
  res.render('index');
});

app.listen(3000);

接著要在應用程式的目錄下,建立預設的翻譯檔和存放路徑(locales/dev/translation.json):
{
  "example": {
    "string1": "Hello World!"
  }
}

如果要支援繁體中文(zh-TW),直接建立相應的翻譯檔(locales/zh-TW/translation.json):
{
  "example": {
    "string1": "哈囉 世界!"
  }
}

然後就可以在 template 中使用之前所定義的字串(views/index.jade):
span= t('example.string1')

完成之後,i18next 會自動依照用戶瀏覽器的語系設定,回應相對應的頁面。


後記

文章一開頭有提到,i18next 可以配合 jquery 使用,有興趣的人可以直接參考『官方說明』。

2012年1月7日 星期六

【企業顧問咨詢】為您的產品選擇作業系統解決方案

Standard
你將為自家的產品,選用什麼樣的作業系統?我想,這年頭,不外乎是 Android。但是,可能不是最佳選擇。

我想大多數人都會同意,由於智慧型行動裝置的掘起,Google Android 在業界大放異彩,使用 Linux 作業系統核心的 Android,實現了許多華麗的UI,也讓眾多廠商取得入門門票,有機會與 Apple iOS 一爭長短。許多人都相信,未來是 Android 的天下,除了手機要用 Android,電視要 Android,救人一命的醫療器材也要用 Android,所謂的 Android anywhere 是終極目標。

不過,經過這幾年下來,許多人發現這不過是我們資訊科技業一廂情願,有太多的領域是難以使用 Android,如工控市場等,無論是以穩定、價格、移植維護成本考量的因素,都可能是無法使用 Android 的重點。因此,如今要選擇作業系統解決方案,又變成一個需要思考的難題。

筆者擔任顧問時,每當面對客戶躊著於這個問題時,我總問他們幾個大問題:
  1. 你們的產品用途是否單一且單純?是否要有讓使用者自行上網安裝其他第三方軟體的擴充性?
  2. 你們的產品是否為消費行電子產品?是否需要高可用行和高穩定性?
  3. 你們的軟體需要有華麗特效的 UI 嗎?你們現在擁有的人員有哪些開發 UI 程式的經驗?
  4. 有網路的需求嗎?如果有,需要哪些需求?Ethernet、Wifi、3G?
  5. 計劃中的產品硬體規格?
  6. 有多少規模的人可以參與開發?

很明顯的,對於手機和行動裝置業者是這樣的答案:(他們都會選用 Android)
  1. 用途不單一,需要讓使用者任意自行上網安裝軟體。
  2. 是功能複雜的消費性電子產品,所以客戶多多少少能接受偶爾當機
  3. UI 需要有華麗的特效。開發人員都熟悉並使用 Java 的經驗。
  4. 有所有網路的需求。
  5. 擁有一定等級以上或是當前最頂級的 ARM 處理器。
  6. 最少數十個,多半是上百人甚至上千人的研發人員。


如果你是『非手機』和『非行動裝置』業者,你的產品可能不完全具備這些條件。建議您,一旦有任何一點不具備,請慎重考慮是否使用 Android 還是有機會選擇其他的解決方案。

而當前的解決方案主要常見有三種:
Android
Linux + Qt Framework
Linux + Own Application

限於篇幅,太細節的評估無法一一列舉,也要視實際狀況而定,但這邊有個簡易的初步評估方法可以提供參考:
檢視產品的用途是否單純

如果你的產品用途不單純,應用不單一且不易規範使用範疇,Android 會是你首要的選擇。

此外,假設你們的產品用途非常單純,又或者是功能並不複雜,建議您可以採用非 Android 的解決方案。理由如下:
  1. Android本身有許多公認的問題,運氣好沒事,運氣不好這些問題都需要自行解決,由於可能出問題的範圍相當廣,要確保有足夠資源處理這些問題。
  2. Android有太多版本且隨時可能會有更新和問題修正,再加上上述問題,開發過程中不可能不追加資源去跟進或監視 Android 當前的狀態。
  3. 如果從產品開發開始到開發結束,都需要人力資源去維護和跟隨,這無疑是一種人力上的消耗。
  4. 到此為止還沒提到正題,產品本身應用的開發,在處理好以上問題之後,才開始準備進入正軌。

很多人常說,使用 Android 開發產品的時程是 Debug 的時間比寫軟體的時間多,有時甚至多達 10:1 都不為過。對於行動裝置廠商來說,整個系統就是產品的整體,花大量資源去維護可以說是理所當然的,但是對非行動裝置廠商來說,這就是額外的開銷甚至是條不歸路,除非您也想找筆者救火(但是筆者當救火員的日子可能不多了,笑)。

後記

如果您也正在煩腦選擇作業系統的問題,希望這本文能幫上忙。

2012年1月6日 星期五

NodeJS 與 MongoDB 的邂逅

Standard
雖然 NodeJS 的模組和開發資源相當多,但相關文件卻非常不足或是不完整,多半文獻都只著重於基礎的使用和片斷的說明,如果不去看原始程式碼,使用 NodeJS 來完成實用的網站,會有很大的困難度。對於已經有過 Web 開發經驗的人,轉換使用 NodeJS 不免也需要花一番功夫,過去經驗中許多的常用的功能,都仍要一一花大量時間嘗試才得以解決。而這樣的情況,對於開發者來說相當的糟,也是很多人重新再評估是否使用 NodeJS 的重點因素之一。有道是『一人得道,雞犬升天』,因此筆者未來將嘗試將自己的實際經驗,寫成一篇篇重點功能實作的文章和隨 Copy 即用的範例,減少其他人浪費同樣的時間再摸索。

開發一個 Web 應用程式,最重要的莫過於資料庫的使用,過去 PHP 有 MySQL 當最佳夥伴,而現在 NodeJS 有 MongoDB 做最佳的組合。MongoDB 是 NoSQL 的代表之一,其採用 JSON/BSON 當做資料儲存和溝通的格式,亦使用 JavaScript 做為 Server-side 的執行程序語言(相當於傳統 RDBMS 的預儲程序),一切設計和習慣與 NodeJS 搭配使用起來,簡直絕配。若你對 MongoDB 的一些基本操作有疑問,可以先參考舊文『MongoDB 快速筆記』。


使用 MongoDB

MongoDB 擁有 NoSQL 的普遍特色,不用預先定義 Schema,所有的 database 和 collection(相當於傳統 RDBMS 的 Table),都會在新增資料後,自動被建立,我們只要專注於使用 NodeJS 操作資料庫即可。

要在 NodeJS 裡使用 MongoDB,可以安裝 mongodb native driver,若透過 npm 來安裝:
npm install mongodb

然後可以使用 NodeJS 建立 MongoDB connection pool ,做一些基礎的操作:
var mongodb = require('mongodb');

var mongodbServer = new mongodb.Server('localhost', 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db('mydb', mongodbServer);

/* open db */
db.open(function() {
    /* Select 'contact' collection */
    db.collection('contact', function(err, collection) {
        /* Insert a data */
        collection.insert({
            name: 'Fred Chien',
            email: 'cfsghost@gmail.com',
            tel: [
                '0926xxx5xx',
                '0912xx11xx'
            ]
        }, function(err, data) {
            if (data) {
                console.log('Successfully Insert');
            } else {
                console.log('Failed to Insert');
            }
        });

        /* Querying */
        collection.find({ name: 'Fred Chien' }, function(err, data) {
            /* Found this People */
            if (data) {
                console.log('Name: ' + data.name + ', email: ' + data.email);
            } else {
                console.log('Cannot found');
            }
        });
    });
});
註:這是 MongoDB 的基本常識,每當新增一筆資料,MongoDB 會自動幫該筆資料加上 _id 欄位,並給與一個唯一值(格式是 ObjectId),所以我們不需要像過去 使用 SQL Server 一般,自己刻意去定義一個 ID 欄位。


預設的 ObjectId 範圍太小,改用 UUID 來當資料的唯一 ID

如果你過去有過 Web 開發經驗,到這邊肯定會開始有一些疑問欲求解,第一個問題肯定是『ObjectId 的數量極限?』。筆者在此不會回答這問題,因為這答案並不重要,想要準確知道答案,可以去『mongodb.org』尋找答案。

比起上述問題,相信你應該更想問:
預設的 ObjectId 適用的範圍?
如果日後資料庫要擴展(Scale),是否有其他的 ID 解決方案可使用?

一般情況, MongoDB 預設的 ObjectId 就相當夠用了,但如果你是要建構大型的 Web Service 或是保留未來的擴充性,可使用 UUID 去代替 ObjectId。不過,因為 MongoDB 本身並不生成 UUID,若是要使用 UUID,就必需先自行產生好 UUID,然後在新增資料時指定生成好的 UUID 給 _id 欄位,讓 MongoDB 改用我們給的 ID 而不使用預設生成的 ObjectId。

因為要自行產生 UUID,必需先為 NodeJS 安裝模組 node-uuid:
npm install uuid

然後生成 UUID 並在 insert 時使用:
var uuid = require('node-uuid');
var mongodb = require('mongodb');

var mongodbServer = new mongodb.Server('localhost', 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db('mydb', mongodbServer);

/* open db */
db.open(function() {
    /* Select 'contact' collection */
    db.collection('contact', function(err, collection) {

        /* Generate UUID(16 Bytes) and convert to BinaryData object for mongodb */
        var uuidBinary = new Buffer(uuid.v1({}, []));
        var id = mongodb.BSONPure.Binary(uuidBinary, mongodb.BSONPure.Binary.SUBTYPE_UUID);

        /* Insert a data with uuid */
        collection.insert({
            _id: id,
            name: 'Fred Chien',
            email: 'cfsghost@gmail.com'
        }, function(err, data) {
            if (data) {
                console.log('Successfully Insert');
            } else {
                console.log('Failed to Insert');
            }
        });
    });
});

你可能會發現,在上面的範例程式中,我們將 UUID 轉成 MongoDB BSON 的 BinaryData 格式,這是為了效能考量,因為用純字串當做 Unique ID,在資料庫搜尋上會比 BinaryData Object 慢很多。


儲存時間戳(Timestamp)

關於儲存時間的問題,如果你去各大 MongoDB 討論區詢問或查詢,通常大家都會告訴你不必做這件事,因為每一筆資料被建立後,自動產生的 ObjectId 就包含了建立的時間訊息,我們只要去學習如何從中去解析時間即可。但是,不單只是建立時間,有時我們會為資料加上各種不同的時間戳,如:更新時間等,所以,儲存時間戳還是必要的。

雖然網路上相關 NodeJS 範例並不多,但 MongoDB 確實有 Timestamp 的資料結構可以用,我們可以這樣使用:
var uuid = require('node-uuid');
var mongodb = require('mongodb');

var mongodbServer = new mongodb.Server('localhost', 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db('mydb', mongodbServer);

/* open db */
db.open(function() {
    /* Select 'contact' collection */
    db.collection('contact', function(err, collection) {

        /* Generate Timestamp and convert for mongodb */
        var ts = new Date().getTime();
        var i = ts % 1000;
        var t = new mongodb.BSONPure.Timestamp(i, Math.floor(ts * 0.001));

        /* Insert a data with uuid */
        collection.insert({
            name: 'Fred Chien',
            email: 'cfsghost@gmail.com',
            created: t
        }, function(err, data) {
            if (data) {
                console.log('Successfully Insert');
            } else {
                console.log('Failed to Insert');
            }
        });
    });
});


建立資料庫索引(Index)

過去有接觸過資料庫的人應該都很清楚,索引(Index)是能優化資料查詢速度的重要功能,MongoDB 同樣也有索引的設計。

可以在 NodeJS 中,這樣為 name 欄位加上索引:
collection.createIndex({ name: 1 });


後記

到此為止,你應該能開始盡情使用 NodeJS + MongoDB 開發 Web Service 了,開始享受完全的 JavaScript 開發人生吧!

2012年1月2日 星期一

苦其心志,勞其筋骨後,世界末日年快樂!

Standard
2012 新年快樂!今年格外不同,也是人稱世界末日將來臨的一年,而在這人生將結束的一年,不免開始思考今年的自我期許。 :-)

回顧去年的風雨,當了一年的『救火隊長』。曾經為了救援朋友的案子,連續三個月,每天平均睡不到一小時,且平均連續工作達72小時以上,如果過程中突然一命嗚乎,一點也不意外。還好,這樣可怕的惡夢,都一一挺了過去,雖然身體似乎出現了點問題,不時陣痛襲來。不過,老天讓我完好的活下來,是祂已經頒發『超級救火員勛章』的最佳證明。

我總相信,只要有信念和方向,任何困境都是老天賜予的挑戰,每當苦盡甘來,就會有許多更好的機運等著。事實上,當了一年職業救火員不是沒有好處,看到了許多人不為人知面貌,許多業界的問題和盲點,更認清了自己該做的事,也得到了更棒更成熟的點子。甚至,某個程度上,已經不害怕死亡突然降臨。

為什麼過的這樣辛苦?為什麼總是孤單的拼命?是否該檢討一下自己?

還清楚記得,這一年有些人這樣問過我,我都沒有正面回答,直到現在才有時間靜下來思考這個問題,也自我檢討了一番。

如果因為負責任而辛苦減壽,為了理想而拼了命和全世界作對,那我真的想不出有任何一點不正確的地方。我只能推論,許多人不願意辛苦,甚至可以『不負責任』去避免辛苦;許多人沒有懷抱理想,所以沒有拼命的目標,只有隨波逐流的生活。

真的要自我檢討,除了憤世忌俗外,就是我死腦筋的總是依據一件事的『順利和成就』,考量應該做什麼,甚至不是自己的義務,也會執行到底,貫徹著父親所囑付:『幫忙之前叫幫忙,幫忙之後叫責任。』然而,許多人理所當然以為處處都是我的義務和責任,一股惱全加諸於我身上。使我,做會自己勞累不堪,不做則內心不舒服,痛苦萬分。

而這樣的個性,讓我和傳統做生意一點都不搭軋,因為我永遠不愛也不喜歡打『把對手權利變成對手義務』的拉拒戰。在這樣的商場戰爭下,人人都不想執行自己的義務,只想行使自己的權利,甚至讓對方的權利變成自己的權利。不過,雖然不喜歡,但不代表我不懂,每次回歸原點和原貌來看,就會發現『人有多不要臉』和『許多醜陋的臉孔』的戲碼都正在天天上演,過程中的種種掩飾都讓人不敢相信,任誰看了都會臉紅找洞鑽。

如果這樣才能不辛苦不孤獨,我寧可苦一輩子。我也永遠不相信,那樣的結黨,能夠有什麼未來。此外,如此彆扭的生意,也不是我所期望的目標。

過去無數個日子,腦袋從未停止轉動過。啄磨過許多想法,也思考過許多問題,思考中入眠已經是常態。堅信,苦其心志,勞其筋骨,然後,痛痛快快又能無後顧之憂的完成件『應該做的事』,這就是今年的自我期許。