thảo luận Tất tần tật về Go (Golang)

Team mình có những thành viên xây dựng hệ thống cho zalo với zing từ ngày đầu nhé, stack của bọn mình là layer storage c++ ( cái này không ngôn ngữ nào có thể thay thế rồi) , service ( golang is best choice), frontend reactjs. Đây là 1 dự án của bọn mình nhé https://butta.vn/ . Ns chung mảng backend có những cái buộc phải dùng c++ thì ngoài ra dùng golang là best choice , trước mình cũng dev .NEt suốt nhưng h dị ứng với mấy cái máy ảo lắm rồi.
Đấy, người thật việc thật, sản phẩm thật
Đỡ phải nghe mấy anh chưa từng làm sản phẩm thật nhưng xạo lol riết
qZV215Z.png
 
Ngồi đọc blog làm cái quái j, đào sâu suy nghĩ về 1 một ngôn ngữ đi, giải quyết các vấn đề của n là được, ngồi nghe mấy thằnd lập trình viên chém gió khác j đẽo cày giữa đồng đâu. Quan trọng mình cần với công việc thế nào, như t , thấy n cực kì hợp lí, n không cần một cái layer trung gian runtime để chạy là 1 lợi thế cho deploy rất tốt rồi, tiếp đến n build rất nhanh ( điều mà c,c++ ko làm được). Cái nữa là việc import thư viện cực kì đơn giản, khác hẳn với c,c++. Một ngôn ngữ giải quyết được vấn đề về portable, deploy, build thì đó là 1 ngôn ngữ tốt. Các hạ tầng bây h chạy theo microservice, phân chia ứng dụng theo chiều ngang chứ ko còn kiểu stacklayer như trước kia, tóm lại chỉ gồm 3 phần layer rõ ràng , low layer ( liên quan tới hệ thống, lưu trữ, ở đây người ta thường dùng c/c++, các hệ quản trị cơ sở dữ liệu hay nosql đều viết bằng c++ hết), tiếp theo layer service ( go được thiết kế với layer service này , tạo các api bằng beego quá nhanh quá gọn, toàn bộ func cho api quảng trị người dùng t có thể viết trong 2 tiếng đổ lại chỉ bằng beego, cần giao tiếp với lowlayer thì sử dụng thrift cực kì nhanh và ổn định), layer UI,UX là cuộc chơi của các lib hay framework js rồi ko phải bàn. Trong trường hợp có những service cần giữ các connection để realtime thì đã có websocket ( golang có hỗ trợ nhé ), và đặc biệt grpc là thứ được sử dụng nhiều nhất trong các kết nối kiểu này ( phần OTT cty t dùng grpc full). Bảo golang là best choice cho starup hoàn toàn có cơ sở vì những tính năng n đem lại, toàn bộ hệ thống OTT, chat ,video call, socialnetwork, livestream ,notification của cty đều được build up theo kiến trúc microservice với bộ 3 c++, golang , js, hệ thống luôn mượt mà và trơn chu nhé, khả năng mở rộng tới hàng tỉ user còn được ( đó là thế mạnh của microservice)
 
Ngôn ngữ càng bị chửi nhiều chứng tỏ là càng nhiều người dùng, chuyện quá bt.

Chửi 1 ngôn ngữ thì phải nói được use case của các bạn là j. Kn của tôi maintain critical backend microservices, qps 20k chạy ngon trên cluster 80 instances, mỗi ngày xử lý gần 10 triệu transaction, weekly deployment ầm ầm.

Đội dev nhiều người, cần collab nhiều, thì ngôn ngữ nó phải đơn giản, hạn chế những tính năng hack não để tập trung vào dev business feature. Còn 1 người, 1 service thì code go thấy thiếu thiếu, gượng gượng rồi chửi, tôi nghĩ chắc cũng bt.
Use the right tool for the right job. Vậy job của bạn là j? Mà chỉ thấy bàn về tool?

Đồng ý với bạn này.

Thật ra để hỏi ngôn ngữ XYZ nào tương lai sáng không thì chả có một cái foresee nào đủ mạnh để xác thực cả đâu.
Mình lấy ví dụ một ngôn ngữ đã có thời là hot trend nhé: Ruby on Rail. Cách đây 8-10 năm trước thì nó khá hot vì build nhanh một cái web trên nền MVC. Rồi lương ở VN cho Ruby vì thế tăng ào ào trong giai đoạn 2011-2015, nếu không nói quá giai đoạn đó nhiều cty sẵn sàng trả mức lương 1k$ cho người chỉ biết 1 năm exp, nhưng rồi cũng theo thời giai người ta lại nhảy ra chuyển qua Node, giờ nhìn lại thị trường còn bao nhiêu cty ở VN đang hunt Ruby đâu.

Thế nhưng những người giỏi Ruby ở giai đoạn đó thì sao? Không lẽ chết đói? Câu trả lời không đâu, người giỏi người ta lựa chọn ngôn ngữ vì sở thích nhưng kiến thức mới là thứ họ xây dựng. Chủ topic có sài XYZ ngôn ngữ nào, làm dự án nào nhưng tất cả đều xoay quanh kiến thức IT chung cả thôi, đó mới là thứ giúp bạn kiếm ra tiền, chứ kn viết một ngôn ngữ ko giúp bạn đi xa được.
 
Beego so với Echo và Gin thì thế nào hở bác? :adore:
Beego ko nhanh hơn nhưng được cái support tốt hơn, viết api đơn giản hơn, cộng động support nhiều hơn, ns chung cứ beego mà táng vì n theo kiến trúc mvc. Gin n đơn giản quá mức nên mình ko thích dùng kiểu đó, beego n hỗ trợ swagger tận răng, tốc độ cũng samesame nhau thôi.
 
Bữa cty mình có 1 case hài vl, má nào code Go sài ko đóng transaction hay sao ấy, nó lên connection poll :LOL:. Sập,
Nói chung Go nó tường mình rõ ràng nhưng code cũng tỉ mỉ tí là OK. Chứ mấy cái vụ transaction bên thằng Hibernate nó lo cho rồi.
Chuẩn go mà ko tỉ mỉ dễ dính mấy cái vụ ko close connection lắm :)))
 
Cái hay của Go tôi thấy là viết UT dễ và sướng, concept interface của nó cũng rất hay: parameter consumer định nghĩa interface của parameter đó, chứ ko bị giới hạn bởi type của parameter -> cảm thấy logic hơn nhiều.

Go đề cao tính thực dụng chứ ko phải đề cao 1 ideology nào, nên lướt qua thì thấy có vẻ ko hấp dẫn, nhưng lúc làm nhiều mới thấy bánh cuốn ae ạ.

Ví dụ:
JSON:
package A
func x(a TypeA){
    a.read()
   //...
}

type TypeA interface{
    read() int
}

Thì khi function x cần parameter có read function thôi chẳng hạn, thì nó sẽ define TypeA ở ngay bên trong package chứa function x, và khi dùng func x, thì có thể pass bất cứ Type nào vào, chỉ cần Type đó có implement function read.

Cách này làm cho package A loosely couple vs user của package A, đồng thời đảm bảo là package A sẽ chỉ dùng những gì cần dùng, và TypeA definition sẽ closer with business function của package A, thay vì của package tạo ra parameter đó.

Cái mà bạn đang nói đến, ko phải Go sáng tác ra đâu, mà thật ra Go impl nó cũng ko tốt hơn gì so với một số ngôn ngữ khác. Để tóm tắt thì có thể đọc về "Prefer Composition over Inheritance" trước.


Java, C# có đầy đủ đồ chơi để impl, khá dễ nhưng ko phải ai cũng làm vì đa số muốn inheritance bởi thiếu kinh nghiệm và hiểu sai về OOP practices. Bản thân dev non tay cũng cho rằng impl Composition to công, rồi ở cty outsource thì cứ bay vào làm tiếp những gì đã có mà ko thắc mắc gì về lớp abstraction này.

Nói tiếp về abstraction thì phần này thuộc về Polymorphic Abstraction, 2 ngôn ngữ khác t biết viết là Rust và Haskell đều hướng programmer đi theo hướng tiếp cận này, thậm chí ko hề đem Inheritance vào ngôn ngữ:

Haskell
Code:
class Show a where
    show :: a -> String

instance Show A where
    show [impl]

Rust
Code:
trait Debug {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
}

impl Debug for A {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    ....
    }
}

Mà code show/display là nằm sẵn trong std core của ngôn ngữ nên dùng được luôn thông qua Derive ko cần phải define + impl như trên, và dev cũng có thể viết luôn những lớp abstraction này để dùng cho các data struct khác derive.
 
Last edited:
Cái mà bạn đang nói đến, ko phải Go sáng tác ra đâu, mà thật ra Go impl nó cũng ko tốt hơn gì so với một số ngôn ngữ khác. Để tóm tắt thì có thể đọc về "Prefer Composition over Inheritance" trước.



Java, C# có đầy đủ đồ chơi để impl, khá dễ nhưng ko phải ai cũng làm vì đa số muốn inheritance bởi thiếu kinh nghiệm và hiểu sai về OOP practices. Bản thân dev non tay cũng cho rằng impl Composition to công, rồi ở cty outsource thì cứ bay vào làm tiếp những gì đã có mà ko thắc mắc gì về lớp abstraction này.

Nói tiếp về abstraction thì phần này thuộc về Polymorphic Abstraction, 2 ngôn ngữ khác t biết viết là Rust và Haskell đều hướng programmer đi theo hướng tiếp cận này, thậm chí ko hề đem Inheritance vào ngôn ngữ:

Haskell
Code:
class Show a where
    show :: a -> String

instance Show A where
    show [impl]

Rust
Code:
trait Debug {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result

impl Debug for A {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    ....
    }
}

Mà code show/display là nằm sẵn trong std core của ngôn ngữ nên dùng được luôn thông qua Derive ko cần phải define + impl như trên, và dev cũng có thể viết luôn những lớp abstraction này để dùng cho các data struct khác derive.
Ông bạn có code Go chưa?

Code:
func main() {
    a := Employee{
        FirstName: "Trung",
        LastName: "Pham",
    }
    b := Person{
        Name: "Trung Pham",
    }
    printName(a)
    printName(b)
}

func printName(nameInstance NameType){
    fmt.Println(nameInstance.GetName());
}


type NameType interface{
    GetName() string
}
Code:
type Employee struct {
    FirstName   string
    LastName    string
    TotalLeaves int
    LeavesTaken int
}


func (e Employee) GetName() string {
    return e.FirstName + " " + e.LastName
}
// ... Other Employee functions

Code:
type Person struct{
    Name string
}

func (p Person) GetName() string {
    return p.Name
}

// ... Other Person functions

3 cái ở trên ở 3 package khác nhau. Cái hay ở đây là function printName có thể tuỳ ý define interface mà nó muốn nhận, và ko cần biết những chi tiết khác của Employee hay Person. Và nó có thể define 1 cái interface vs naming phù hợp vs cái business scope của nó.

Cái hay ở đây là consumer, ở đây là package chứa printName và producer là package chứa Person vs Employee đều có thể định nghĩa business type mà nó muốn, và cả 3 hầu như ko cần phải biết đến sự tồn tại của nhau, so với Java hay C++, thì type phần lớn được định nghĩa bởi producer package, hoặc là sẽ phải có coupling giữa consumer và producer.

Demo: https://play.golang.org/p/lHUnNCMkIRS

Hay ý ông là package implement function printName phải wrap Employee vs Person bằng wrapper class để làm composition? Như vậy cũng được, tuy nhiên sẽ phải mất thêm 1 layer để translate giữa 3 package vs nhau, rất mệt mỏi.

Chi tiết nhỏ thôi, nhưng làm mới thấy có giá trị.
 
Last edited:
Ông bạn có code Go chưa?

Code:
func main() {
    a := Employee{
        FirstName: "Trung",
        LastName: "Pham",
    }
    b := Person{
        Name: "Trung Pham",
    }
    printName(a)
    printName(b)
}

func printName(nameInstance NameType){
    fmt.Println(nameInstance.GetName());
}


type NameType interface{
    GetName() string
}
Code:
type Employee struct {
    FirstName   string
    LastName    string
    TotalLeaves int
    LeavesTaken int
}


func (e Employee) GetName() string {
    return e.FirstName + " " + e.LastName
}
// ... Other Employee functions

Code:
type Person struct{
    Name string
}

func (p Person) GetName() string {
    return p.Name
}

// ... Other Person functions

3 cái ở trên ở 3 package khác nhau. Cái hay ở đây là function printName có thể tuỳ ý define interface mà nó muốn nhận, và ko cần biết những chi tiết khác của Employee hay Person. Và nó có thể define 1 cái interface vs naming phù hợp vs cái business scope của nó.

Cái hay ở đây là consumer, ở đây là package chứa printName và producer là package chứa Person vs Employee đều có thể định nghĩa business type mà nó muốn, và cả 3 hầu như ko cần phải biết đến sự tồn tại của nhau, so với Java hay C++, thì type phần lớn được định nghĩa bởi producer package, hoặc là sẽ phải có coupling giữa consumer và producer.

Demo: https://play.golang.org/p/lHUnNCMkIRS

Hay ý ông là package implement function printName phải wrap Employee vs Person bằng wrapper class để làm composition? Như vậy cũng được, tuy nhiên sẽ phải mất thêm 1 layer để translate giữa 3 package vs nhau, rất mệt mỏi.

Chi tiết nhỏ thôi, nhưng làm mới thấy có giá trị.

Vậy bạn có biết Go dựa vào gì để bạn có thể implicit define/specify Interface như vậy ko? Tại sao bọn khác nó vẫn dùng name based bắt buộc programmer phải explicit define trước khi impl ko (Java, Scala, Rust...)

Type/Function assertion đối với kiểu implicit define này của Go chưa đủ tốt để dev hoàn toàn dùng được mà ko lo bug. Và cũng có vài người explode được rồi đó, họ cho rằng đây là design flaw của Go luôn.

Giống như bạn dùng dynamic type lang rồi nó cho bạn flexibility nhưng trade-off cả đó chứ tưởng flexibility mà ngon đâu.
 
Chi tiết nhỏ thôi, nhưng làm mới thấy có giá trị.
Tôi thấy được cái này mất cái kia.
1. Bình thường nếu tôi khai báo expicit thì cần implement 1 interface IDE có thể gen tất cả các method cần implement cho tôi. Dùng structural typing này thì cái IDE đem đi vứt. (chả lẽ lại bật source của cái interface lên rồi copy paste sang :rolleyes: )
implement.png

2. Khai báo từng minh thì tôi dùng IDE refactor hàng trăm implement của 1 interface cái một và tôi sure 100% là đếch sót cái nào, còn kiểu implicit như trên thì IDE đem đi vứt (trừ phi nó list hết tất cả các class chứa method của cái interface đó và tôi phải đi soi lại từng cái một). Anh nào hay refactor code codebase to to tí thì cái này là vô cùng cần thiết, tôi đếch muốn ngồi cả ngày để debug chỉ vì rename 1 cái method
3. Tôi muốn impl 1 cái interface mà lỡ gõ sai chính tả thì chắc phải đợi lúc compile mới biết (mà giả sử cái class đó chưa được gán vào đâu hết thì chắc compile cũng xuôi lọt luôn).
Trong khi nếu anh khai báo tường minh:
correct.png

3. Mấy cái DI Container dựa trên type chắc anh Go cũng không impl được vì biết đc class nào thuộc về interface nào đâu.
4. Tôi muốn xem 1 interface có những impl nào thì cũng chịu chết (chả lẽ 1 cái interface có method read nó list ra cả trăm cái class không liên quan cho tôi xem ?).
map.png

5. Auto complete cũng chịu chết, dùng implicit thì anh Go có suggest được tốt thế này không:
suggest.png

suggest2.png

6. Khai báo từng minh thì tôi đọc source cũng dễ hơn, đọc 1 cái class thấy nó implement cái interface nào là biết ngay cái method đó để làm gì liền, cái class đó có thể xài trong context nào liền.
Nói chung theo kiểu anh Go thì thằng IDE chẳng khác gì phế vật.
Ở đây tôi bàn về khía cạnh tooling thôi, xài mấy ngôn ngữ typed mà IDE nó cứ trơ người ra thì rất bực. Hồi đó tôi code thử Go trên goland thì ôi thôi, xài IDE như không xài, chả khác gì đang code JS. Code vài dòng là phải bật docs lên để xem vì không biết interface này thằng nào đang impl để mà ném vô, trong khi Java, Kotlin tôi đếch cần phải nhìn docs luôn vì IDE nó suggest, gen code hết rồi.
Nói chung type system ngoài để check type thì còn để support IDE, khai báo càng tường minh thì IDE nó biết anh đang muốn gì mà còn support, mang tiếng là static typing mà IDE cứ trơ người ra thì code khác mịa gì Dương Quá đâu :cautious:. Nói chung lập trình thì càng explicit càng tốt, code không phải chỉ có 1 mình bạn đọc mà còn cho người khác, cho IDE nó đọc nữa. Mấy anh cứ chửi thằng Java verbose chứ codebase to lên tí thằng nào cũng quay đầu về với anh Java cả
 
Last edited:
Sáng chứ, nhiều công ty lớn, unicorn startups sử dụng với cả performance vs cú pháp rất gần gũi nữa. Có điều ecosystem còn tưởng đối nhỏ, bộ thư viện còn ít hơn so với các nn khác như java, .net, nhất là trùm node :byebye: (deno mới ra cũng thú vị ấy), một phần nó cũng hơi kén và phù hợp với dự án siu to khổng lồ hơn nên mình ko nghĩ cộng đồng sẽ đc như nodejs. Hiện tại cũng đang code go và đương nhiên sẽ chọn nó nếu dự án siu to khổng lồ, còn không xài gì cũng đc :smile:
 
A nào tính dùng beego thì nên cân nhắc vì Orm thằng này t đc report là tệ lắm : global lock, mapping ngu chưa kể có version MR còn quên close connection.
 
Beego là tốt nhất rồi, mấy cái khác toàn kiểu micro fw chứ ko phải fullstack fw.

Bác trên kia làm ở Vin xài beego, ko biết performance benchmark thế nào nhỉ? Mấy cty to to mình làm qua thì đều ko dùng fw và ORM, lý do là performance kém do phải dùng reflection quá nhiều.
 
Vậy bạn có biết Go dựa vào gì để bạn có thể implicit define/specify Interface như vậy ko? Tại sao bọn khác nó vẫn dùng name based bắt buộc programmer phải explicit define trước khi impl ko (Java, Scala, Rust...)

Type/Function assertion đối với kiểu implicit define này của Go chưa đủ tốt để dev hoàn toàn dùng được mà ko lo bug. Và cũng có vài người explode được rồi đó, họ cho rằng đây là design flaw của Go luôn.

Giống như bạn dùng dynamic type lang rồi nó cho bạn flexibility nhưng trade-off cả đó chứ tưởng flexibility mà ngon đâu.
Comment của ông chung chung quá. Mind to elaborate? Việc lựa chọn trade off là chuyện bt của swe, làm j có solution hoàn hảo.
 
Beego là tốt nhất rồi, mấy cái khác toàn kiểu micro fw chứ ko phải fullstack fw.

Bác trên kia làm ở Vin xài beego, ko biết performance benchmark thế nào nhỉ? Mấy cty to to mình làm qua thì đều ko dùng fw và ORM, lý do là performance kém do phải dùng reflection quá nhiều.
từ khi rời .Net tới golang mình còn không có khái niệm orm là gì nữa. Vì tất cả db mình dùng đều là noSQL như leveldb, rockdb, redis hay mongodb, mysql là một cái j đó thật xa lạ là dài dòng
 
Team mình có những thành viên xây dựng hệ thống cho zalo với zing từ ngày đầu nhé, stack của bọn mình là layer storage c++ ( cái này không ngôn ngữ nào có thể thay thế rồi) , service ( golang is best choice), frontend reactjs. Đây là 1 dự án của bọn mình nhé https://butta.vn/ . Ns chung mảng backend có những cái buộc phải dùng c++ thì ngoài ra dùng golang là best choice , trước mình cũng dev .NEt suốt nhưng h dị ứng với mấy cái máy ảo lắm rồi.
tôi không hiểu bạn quote tôi làm gì? bảo là bên tôi dùng golang cho nên golang là nhất? hay nói vì tôi dùng golang cho nên performance của golang cao?
 
thằng nodejs mới tởm lợm, tuy là dynamic language mà performance tương đương với mấy thằng compiled.
cơ mà tôi dùng quen ruby/elixir có stdlib tử tế rồi quay lại mấy thằng nodejs hay golang thấy bất tiện vãi, riêng mấy cái basic function để manipulate array/hashmap đã không bằng.

mà nói cái này lại nhớ thằng swift, mọi thứ đều ngon cơ mà cái stdlib như đấm vào mặt, ví dụ tôi hay evaluate language bằng mấy cái tính năng cơ bản như read file by lines các kiểu, thì trong crystal tôi chỉ cần `File.read_lines(file_path)` là được, swift thì
Code:
if let path = Bundle.main.path(forResource: "TextFile", ofType: "txt") {
    do {
        let data = try String(contentsOfFile: path, encoding: .utf8)
        let myStrings = data.components(separatedBy: .newlines)
        TextView.text = myStrings.joined(separator: ", ")
    } catch {
        print(error)
    }
}
à mà cái này là 3.0 còn đỡ, swift 2.0 mới là cái tôi chửi:
Code:
    do {
        if let path = NSBundle.mainBundle().pathForResource("TextFile", ofType: "txt"){
            let data = try String(contentsOfFile:path, encoding: NSUTF8StringEncoding)

            let myStrings = data.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
            print(myStrings)
        }
    } catch let err as NSError {
        //do sth with Error
        print(err)
    }
tuy rằng có IDE nó đỡ, cơ mà nhìn mấy cái function name hay constant đều mất mẹ cảm tình :censored:

anh go cũng chả khá hơn
Code:
package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}
(mấy cái này copy từ SO)
nhìn đã mất cảm tình :censored:
Ông suy nghĩ vậy ngược với tôi vãi, tôi lại thấy cách mà ông nói là fail lại dễ hiểu, dễ dùng, cho performance tốt hơn, về vấn đề viết nhanh hay chậm cũng ko quan trọng lắm
 
Back
Top