新的工作,前幾天剛任職滿一個月
前幾個月心中浮出念頭,想換工作,就像馬雲說的,心委屈了,給的不到位
直到最後真的不想再承受的是,我真的由衷的喜歡寫程式,學新的東西,解決問題
後端用 Python, Node.js , 或是 Golang 去刻,也可以接受被叫去支援寫 Android
, iOS 的應用程式,可是 PHP Team 的工程師離職後,找不到人,找我去接專案,心中真的很圈圈叉叉
好歹我也是掛在 Python 的開發 Team 下面,應該在怎麼凹,也凹不到我吧!
喜歡寫程式,愛 Linux ,也尊重 Php 的工程師,寫過1, 2 年
可是無奈現在看到程式裡有很多的錢字號,真的要很多錢的工作職務,才吞的下去,尤其是人員的離職
就亂塞專案,心裡真的很不舒服,就是這樣的感覺,淹沒我工作的樂趣,及下班後,陪伴小孩,家人的幸福感,
開始認真的再看看有沒有適合的工作機會,這裡分享中年程式宅,換工作的心得,由幾個點來看找工作
這件事
最好的工作,一定是朋友介紹的,不過阿宅的朋友一般不多,所以自己拿捏一下
看工作說明及前置作業
1. 一般的說明,寫的很制式的,就不用投了,一間公司,連找人才這麼重要的事,都不在意了,相信也不太會重視你
2. 專業名詞,幾乎全部出現了,薪資待遇卻沒有空間,這投的話,只能說,自己的邏輯判斷都有問題了怎麼寫程式
3. 說明裡提到,克苦耐勞,新人尤佳,一般台灣的徵才有很多淺規則,要克苦耐勞的一定只有更苦,新人尤佳的
其實就是要應屆畢業的同學,說到歡迎抬青椒成的,就是只接受台青椒成 (外商不適用,真正的外商比較沒有這些規則)
4. 一般台灣企業,或是假外商的話,條件裡的 nice to have ,不用懷疑,就是一定要會,而且要很厲害
5. 新創公司,一般除了是自己認識的人外,一定要選成立一年以上的公司,一間連員工年終都沒發過的公司,你信任嗎?
6. 一般新創公司,徵才的文案寫的很漂亮,有一個陷阱是薪水的範圍給的很大,這種通常只是吸引你投履歷,一般給不起上限
(除非你有學長姊,或是認識的朋友在裡面任職,不過通常是好缺的話,不用寫到文案,履歷已經很多份,正送到人資主管手上了)
7. 大公司,有好學歷,比較有好機會,公司經營者越是學術界出身的越在意學歷,自然你學歷是強項,就很有機會出線,如果你是專業強,
學歷像我一樣,私立大學還非本科系,通常外商或是,專業強的老闆,會比較重視你
8. 一個工作一直在徵人,可能掛個一年以上,我是沒投過這樣的職務,你可以問看看,在告訴大家
9. 公司的經營者,名聲不好,或是人品不好,這個其實很主觀,就像選舉一樣,反正自己的選擇,我是不會考慮
10. 另外就是 Head hunter 及 Linkedin 提供的機會,一般會比一般人力銀行的條件好一些
進入面試過程,其實這是很主觀的事情,只能說,千奇百怪,什麼人都有
有些人,希望你的智力破表,人格正常,專業超強,充滿創造力及想像力,又要好管理,聽了是不是很矛盾 :-)
反正就是做自己,假如你找的是一份你本來就喜歡做的事,有問題就問,不能接受的就不要接受,
不必像一些面試法則說的那樣,溫文儒雅,有禮貌,懂規矩,少問問題,我們求得是一份快樂的工作,
工作開心,自然很期待每天上班,不是奴才的缺,每天痛苦的工作
如果想知道,新的工作環境,是不是責任制,或是一般的下班時間,就直接問,會因為這樣就在意的主管,
一般他也不是在意你的專業能力,而是在意對你的控制能力,也許教你面試的書,會說你失去了一的工作機會
其實是你又浪費了寶貴的時間在錯的地方
1. 一般面試官,會問的,有工作經驗,答的出來,知道多少就說多少,謙虛,不要唬爛,不要自以為是
2. 程式題,就盡量寫,不會寫,也還好,就說一下怎麼解,或是自己的了解,依您自己開發多年的經驗,你的成果應該都不是在紙上寫出來的吧!
一般程式題,我覺得是要過濾一些完全沒有概念的人用的,真的會用很艱深的程式問題或是演算法,來考倒你,我想這樣公司需要的是很會考試的同學
一般網路上,找的到的,或是書本有教的,查的到的,有概念,需要驗正及實作的時候,再專研即可,真正的工作,會有不同的挑戰,不見得書上找的到,
有時候,是真的花比較多時間在看文件,及實作
3. 一般回答完面試官所有問題後,會給你提問,如果你真的是會不好意思,我也是會有一點,我至少問
a. 公司一般作息時間,是不是責任制 ( 一般會要你加班工作的主管,聽到你這樣問,也會打你槍,不過是好事)
b. 公司有沒有不同於勞基法以外特別的規定
c. 問面試官,現在任職的公司服務,最令他覺得驕傲,自豪的點,還有最不舒服的點 (說不出來,或是吱吱嗚嗚,自己都說不出為什麼要做下去,又怎麼能要你加入,大概就可以打槍這一間公司不用再往下談了)
d. 公司成家的工程師的比例,及一間公司工程師的比例 (一般,日夜都忙的工作,沒有時間交女友,或是生活只有工作,沒有一點其他的生活,又有誰要跟他共組家庭)
最後階段,公司覺得你是不可多得的人才,談薪資及 package
1. 如果是要離開台灣,記得要依照,實際國家水準,不要用台灣的角度看
2. 千萬不要相信人力銀行上面的數據,用你簡單的數學,如果,不吃不喝,買不起房子的薪水,哪就不要做了,出國吧
3. 至少相信自己的專業,在 odesk 上面可以拿的到的時薪,稍微還原一下,就可以推出一個正常的無國界工作者的基本價值,不然就開始接案人生吧 ;-)
4. 在自己真正有實力的基礎下(不是自我感覺良好),至少,你覺得爽,覺得夠,不然又會墜入心理的糾結 (最好可以跟學長姊,或是同業的朋友打聽一下,不要看人力銀行的數據)
以上自己的心得分享,希望對您有幫助
prism
2014-12-24
2014-12-23
golang routers
覺得簡單易懂的好文章
可以讓你用 golang 寫 api 有一個簡單的開始
http://nicolasmerouze.com/guide-routers-golang/
另外也推薦他的 gist ,再把 main 的部份,拔出來,做一個 NewRouter 應該更好測試,再加幾個函式庫,就是一個 micro framework 了
作者這一系列的文章 http://nicolasmerouze.com/build-web-framework-golang/
相關也推再看 alice 模組作者的文章,雖然 alice 很單純,單純到自己刻,可能也只有 5 行左右
不過看一下,作者的實作概念,挺不錯的
https://justinas.org/alice-painless-middleware-chaining-for-go/
https://github.com/justinas/alice
https://github.com/julienschmidt/httprouter
https://github.com/gorilla/context
可以讓你用 golang 寫 api 有一個簡單的開始
http://nicolasmerouze.com/guide-routers-golang/
另外也推薦他的 gist ,再把 main 的部份,拔出來,做一個 NewRouter 應該更好測試,再加幾個函式庫,就是一個 micro framework 了
作者這一系列的文章 http://nicolasmerouze.com/build-web-framework-golang/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"database/sql" | |
"encoding/json" | |
"errors" | |
"fmt" | |
"log" | |
"net/http" | |
"time" | |
"github.com/gorilla/context" | |
"github.com/julienschmidt/httprouter" | |
"github.com/justinas/alice" | |
) | |
func recoverHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
defer func() { | |
if err := recover(); err != nil { | |
log.Printf("panic: %+v", err) | |
http.Error(w, http.StatusText(500), 500) | |
} | |
}() | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func loggingHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
t1 := time.Now() | |
next.ServeHTTP(w, r) | |
t2 := time.Now() | |
log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1)) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func aboutHandler(w http.ResponseWriter, r *http.Request) { | |
fmt.Fprintf(w, "You are on the about page.") | |
} | |
func indexHandler(w http.ResponseWriter, r *http.Request) { | |
fmt.Fprintf(w, "Welcome!") | |
} | |
type appContext struct { | |
db *sql.DB | |
} | |
func (c *appContext) authHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
authToken := r.Header.Get("Authorization") | |
user, err := map[string]interface{}{}, errors.New("test") | |
// user, err := getUser(c.db, authToken) | |
log.Println(authToken) | |
if err != nil { | |
http.Error(w, http.StatusText(401), 401) | |
return | |
} | |
context.Set(r, "user", user) | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func (c *appContext) adminHandler(w http.ResponseWriter, r *http.Request) { | |
user := context.Get(r, "user") | |
// Maybe other operations on the database | |
json.NewEncoder(w).Encode(user) | |
} | |
func (c *appContext) teaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
log.Println(params.ByName("id")) | |
// tea := getTea(c.db, params.ByName("id")) | |
json.NewEncoder(w).Encode(nil) | |
} | |
// We could also put *httprouter.Router in a field to not get access to the original methods (GET, POST, etc. in uppercase) | |
type router struct { | |
*httprouter.Router | |
} | |
func (r *router) Get(path string, handler http.Handler) { | |
r.GET(path, wrapHandler(handler)) | |
} | |
func NewRouter() *router { | |
return &router{httprouter.New()} | |
} | |
func wrapHandler(h http.Handler) httprouter.Handle { | |
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
context.Set(r, "params", ps) | |
h.ServeHTTP(w, r) | |
} | |
} | |
func main() { | |
// db := sql.Open("postgres", "...") | |
appC := appContext{nil} | |
commonHandlers := alice.New(context.ClearHandler, loggingHandler, recoverHandler) | |
router := NewRouter() | |
router.Get("/admin", commonHandlers.Append(appC.authHandler).ThenFunc(appC.adminHandler)) | |
router.Get("/about", commonHandlers.ThenFunc(aboutHandler)) | |
router.Get("/", commonHandlers.ThenFunc(indexHandler)) | |
router.Get("/teas/:id", commonHandlers.ThenFunc(appC.teaHandler)) | |
http.ListenAndServe(":8080", router) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"log" | |
"net/http" | |
"reflect" | |
"time" | |
"github.com/gorilla/context" | |
"github.com/julienschmidt/httprouter" | |
"github.com/justinas/alice" | |
"gopkg.in/mgo.v2" | |
"gopkg.in/mgo.v2/bson" | |
) | |
// Repo | |
type Tea struct { | |
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"` | |
Name string `json:"name"` | |
Category string `json:"category"` | |
} | |
type TeasCollection struct { | |
Data []Tea `json:"data"` | |
} | |
type TeaResource struct { | |
Data Tea `json:"data"` | |
} | |
type TeaRepo struct { | |
coll *mgo.Collection | |
} | |
func (r *TeaRepo) All() (TeasCollection, error) { | |
result := TeasCollection{[]Tea{}} | |
err := r.coll.Find(nil).All(&result.Data) | |
if err != nil { | |
return result, err | |
} | |
return result, nil | |
} | |
func (r *TeaRepo) Find(id string) (TeaResource, error) { | |
result := TeaResource{} | |
err := r.coll.FindId(bson.ObjectIdHex(id)).One(&result.Data) | |
if err != nil { | |
return result, err | |
} | |
return result, nil | |
} | |
func (r *TeaRepo) Create(tea *Tea) error { | |
id := bson.NewObjectId() | |
_, err := r.coll.UpsertId(id, tea) | |
if err != nil { | |
return err | |
} | |
tea.Id = id | |
return nil | |
} | |
func (r *TeaRepo) Update(tea *Tea) error { | |
err := r.coll.UpdateId(tea.Id, tea) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
func (r *TeaRepo) Delete(id string) error { | |
err := r.coll.RemoveId(bson.ObjectIdHex(id)) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
// Errors | |
type Errors struct { | |
Errors []*Error `json:"errors"` | |
} | |
type Error struct { | |
Id string `json:"id"` | |
Status int `json:"status"` | |
Title string `json:"title"` | |
Detail string `json:"detail"` | |
} | |
func WriteError(w http.ResponseWriter, err *Error) { | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
w.WriteHeader(err.Status) | |
json.NewEncoder(w).Encode(Errors{[]*Error{err}}) | |
} | |
var ( | |
ErrBadRequest = &Error{"bad_request", 400, "Bad request", "Request body is not well-formed. It must be JSON."} | |
ErrNotAcceptable = &Error{"not_acceptable", 406, "Not Acceptable", "Accept header must be set to 'application/vnd.api+json'."} | |
ErrUnsupportedMediaType = &Error{"unsupported_media_type", 415, "Unsupported Media Type", "Content-Type header must be set to: 'application/vnd.api+json'."} | |
ErrInternalServer = &Error{"internal_server_error", 500, "Internal Server Error", "Something went wrong."} | |
) | |
// Middlewares | |
func recoverHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
defer func() { | |
if err := recover(); err != nil { | |
log.Printf("panic: %+v", err) | |
WriteError(w, ErrInternalServer) | |
} | |
}() | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func loggingHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
t1 := time.Now() | |
next.ServeHTTP(w, r) | |
t2 := time.Now() | |
log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1)) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func acceptHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
if r.Header.Get("Accept") != "application/vnd.api+json" { | |
WriteError(w, ErrNotAcceptable) | |
return | |
} | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func contentTypeHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
if r.Header.Get("Content-Type") != "application/vnd.api+json" { | |
WriteError(w, ErrUnsupportedMediaType) | |
return | |
} | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func bodyHandler(v interface{}) func(http.Handler) http.Handler { | |
t := reflect.TypeOf(v) | |
m := func(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
val := reflect.New(t).Interface() | |
err := json.NewDecoder(r.Body).Decode(val) | |
if err != nil { | |
WriteError(w, ErrBadRequest) | |
return | |
} | |
if next != nil { | |
context.Set(r, "body", val) | |
next.ServeHTTP(w, r) | |
} | |
} | |
return http.HandlerFunc(fn) | |
} | |
return m | |
} | |
// Main handlers | |
type appContext struct { | |
db *mgo.Database | |
} | |
func (c *appContext) teasHandler(w http.ResponseWriter, r *http.Request) { | |
repo := TeaRepo{c.db.C("teas")} | |
teas, err := repo.All() | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
json.NewEncoder(w).Encode(teas) | |
} | |
func (c *appContext) teaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
repo := TeaRepo{c.db.C("teas")} | |
tea, err := repo.Find(params.ByName("id")) | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
json.NewEncoder(w).Encode(tea) | |
} | |
func (c *appContext) createTeaHandler(w http.ResponseWriter, r *http.Request) { | |
body := context.Get(r, "body").(*TeaResource) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Create(&body.Data) | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
w.WriteHeader(201) | |
json.NewEncoder(w).Encode(body) | |
} | |
func (c *appContext) updateTeaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
body := context.Get(r, "body").(*TeaResource) | |
body.Data.Id = bson.ObjectIdHex(params.ByName("id")) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Update(&body.Data) | |
if err != nil { | |
panic(err) | |
} | |
w.WriteHeader(204) | |
w.Write([]byte("\n")) | |
} | |
func (c *appContext) deleteTeaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Delete(params.ByName("id")) | |
if err != nil { | |
panic(err) | |
} | |
w.WriteHeader(204) | |
w.Write([]byte("\n")) | |
} | |
// Router | |
type router struct { | |
*httprouter.Router | |
} | |
func (r *router) Get(path string, handler http.Handler) { | |
r.GET(path, wrapHandler(handler)) | |
} | |
func (r *router) Post(path string, handler http.Handler) { | |
r.POST(path, wrapHandler(handler)) | |
} | |
func (r *router) Put(path string, handler http.Handler) { | |
r.PUT(path, wrapHandler(handler)) | |
} | |
func (r *router) Delete(path string, handler http.Handler) { | |
r.DELETE(path, wrapHandler(handler)) | |
} | |
func NewRouter() *router { | |
return &router{httprouter.New()} | |
} | |
func wrapHandler(h http.Handler) httprouter.Handle { | |
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
context.Set(r, "params", ps) | |
h.ServeHTTP(w, r) | |
} | |
} | |
func main() { | |
session, err := mgo.Dial("localhost") | |
if err != nil { | |
panic(err) | |
} | |
defer session.Close() | |
session.SetMode(mgo.Monotonic, true) | |
appC := appContext{session.DB("test")} | |
commonHandlers := alice.New(context.ClearHandler, loggingHandler, recoverHandler, acceptHandler) | |
router := NewRouter() | |
router.Get("/teas/:id", commonHandlers.ThenFunc(appC.teaHandler)) | |
router.Put("/teas/:id", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.updateTeaHandler)) | |
router.Delete("/teas/:id", commonHandlers.ThenFunc(appC.deleteTeaHandler)) | |
router.Get("/teas", commonHandlers.ThenFunc(appC.teasHandler)) | |
router.Post("/teas", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.createTeaHandler)) | |
http.ListenAndServe(":8080", router) | |
} |
相關也推再看 alice 模組作者的文章,雖然 alice 很單純,單純到自己刻,可能也只有 5 行左右
不過看一下,作者的實作概念,挺不錯的
https://justinas.org/alice-painless-middleware-chaining-for-go/
https://github.com/justinas/alice
https://github.com/julienschmidt/httprouter
https://github.com/gorilla/context
2014-12-17
vim paste
網路上大大分享的 vim 技巧
可以自動 toggle paste 模式,我還真的就是那種,要貼
就 toggle 一下的人,果然,懶,才是進步的原動力
http://blog.longwin.com.tw/2014/12/vim-linux-mac-putty-paste-mode-change-2014/
原始來源
http://stackoverflow.com/questions/5585129/pasting-code-into-terminal-window-into-vim-on-mac-os-x/7053522#7053522
可以自動 toggle paste 模式,我還真的就是那種,要貼
就 toggle 一下的人,果然,懶,才是進步的原動力
http://blog.longwin.com.tw/2014/12/vim-linux-mac-putty-paste-mode-change-2014/
原始來源
http://stackoverflow.com/questions/5585129/pasting-code-into-terminal-window-into-vim-on-mac-os-x/7053522#7053522
訂閱:
文章 (Atom)