3

腾小云导读

本文作者在腾讯多年,主要从事的是腾讯云CDN、EdgeOne产品的后台研发工作。作者在云计算领域遇到了不少代码设计和程序设计的问题,他对于如何把项目中的代码标准化、提高开发维护的效率,做了总结梳理。本篇为各位分享作者总结的代码设计、架构设计原则和工作思维。欢迎阅读~

目录

1 万物皆可抽象

1.1 遵循DRY原则

1.2 SOLID 原则

1.3 应用设计模式

2 高可用设计

2.1 设计高可用的系统架构

2.2 日志和监控,包含服务级和业务级

2.3 做好容灾设计 2.4 系统稳定是一名后台开发的生命线

3 想尽一切办法“偷懒”

3.1 优化重复流程、一切自动化

3.2 使用模板和标准化流程

3.3 应用项目管理工具和流程

4 从价值出发,专注于最重要的事

5 沟通与协作也是身为开发的必备技能

6 凡事有交代,件件有着落,事事有回音

7 保持开放和学习的心态

8 职业发展

9 横向发展

01、万物皆可抽象

1.1 遵循 DRY 原则

DRY 原则(Don’t Repeat Yourself),中文直译为:不要重复自己。将它应用在编程中,可以理解为:不要写重复的代码。

或许有些开发者会想当然地认为,只要代码有重复就是违反 DRY 原则了。真的是这样吗?答案是否定的。实际上,重复的代码不一定违反 DRY 原则,而有些看似不重复的代码也有可能违反 DRY 原则。

DRY 原则的典型代码重复情况有三种:实现逻辑重复、功能语义重复和代码执行重复。下面我们简单分享。

1.1.1 实现逻辑重复

以下是一个实现逻辑重复的例子:

class Calculator:
    def add(self, a, b):
        return a + b

    def subtract(self, a, b):
        return a - b

    def multiply(self, a, b):
        return a * b

    def divide(self, a, b):
        if b == 0:
            # ...throw DivideByZeroException...
        pass
        return a / b

class AdvancedCalculator(Calculator):
    def power(self, a, b):
        result = 1
        for i in range(b):
            result *= a
        return result

    def square_root(self, a):
        if a < 0:
            # ...throw InvalidInputException...
            pass
        return math.sqrt(a)

在这个例子中,AdvancedCalculator 继承了 Calculator,但是在 AdvancedCalculator中实现了 power 和 square\_root 方法,这两个方法的实现与 Calculator 中的 multiply 和 divide 方法有很多重复的逻辑。例如,power 方法中的循环可以使用 multiply 方法来实现,square\_root 方法中的判断可以使用 divide 方法来实现。这种重复的逻辑会导致代码冗长、难以维护,并且容易出现错误。

1.1.2 功能语义重复

功能语义重复是指代码中存在相同的功能语义,但是代码的实现不同。例如,以下代码中的两个函数都实现了计算一个数的平方,但是它们的实现方式不同:

def square1(x):
    return x ** 2

def square2(x):
    return pow(x, 2)

可以看到,这段代码中存在相同的功能语义,即计算一个数的平方,但是代码的实现不同。如果我们遵循DRY原则,可以将计算一个数的平方抽象成一个函数,然后在需要计算平方的地方调用该函数,避免了功能语义的重复。

def square(x):
    return x ** 2

1.1.3 功能语义重复

UserService中 login()用来校验用户是否登陆成功,如果失败就返回异常,否则返回用户信息。

class UserRepo:
    def __init__(self, db):
        self.db = db

    def check_if_user_existed(self, email, password):
        if not EmailValidation.validate(email):
            # ...throw InvalidEmailException...
            pass
        if not PasswordValidation.validate(password):
            # ...throw InvalidPasswordException...
            pass
        # ...query db to check if email&password exists...
        
    def get_user_by_email(self, email):
        if not EmailValidation.validate(email):
            # ...throw InvalidEmailException...
            pass
        # ...query db to get user by email...

class UserService:
    def __init__(self, user_repo):
        self.user_repo = user_repo

    def login(self, email, password):
        existed = self.user_repo.check_if_user_existed(email, password)
        if not existed:
            # ...throw AuthenticationFailureException...
            pass
        user = self.user_repo.get_user_by_email(email)
        return user

从上面代码可以发现,login() 调用 UserRepo 的 check\_if\_user\_existed() 和 get\_user\_by\_email(),这两个函数都校验了一次 email 。我们可以将校验代码放到 login() 即可。

另外还有一个隐藏的执行重复问题,login()并不需要调用check\_if\_user\_existed(),只需要调用一次 get\_user\_by\_email() ,从数据库中获取用户的 email、password 等信息跟用户输入的 email、password 信息做对比,依次判断是否登陆成功。

实际上,这样的优化是很有必要的,因为两个函数都查询了数据库,数据库 I/O 操作是比较耗时的,在写代码的时候应当减少这类 I/O 操作。

按照刚刚的思路把代码重构一下:

class UserService:
    def __init__(self, user_repo):
        self.user_repo = user_repo

    def login(self, email, password):
        if not EmailValidation.validate(email):
            # ...throw InvalidEmailException...
            pass
        if not PasswordValidation.validate(password):
            # ...throw InvalidPasswordException...
            pass
        user = self.user_repo.get_user_by_email(email)
        if user is None or password != user.password:
            # ...throw AuthenticationFailureException...
            pass
        return user
class UserRepo:
    def __init__(self, db):
        self.db = db

    def check_if_user_existed(self, email, password):
        # ...query db to check if email&password exists...

    def get_user_by_email(self, email):
        # ...query db to get user by email...

这样做以后,整个代码结构变得更清晰了,类的职责也更加统一。

身为一名合格的程序员,要时刻谨记不干“重复”的事情,能抽象的都做抽象,对后续系统的维护有很大的帮助。

1.2 SOLID 原则

在程序设计领域, SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期引入,指代了面向对象编程和面向对象设计的五个基本原则。当这些原则被一起应用时,它们使得一个程序员开发一个容易进行软件维护和扩展的系统变得更加可能。

1.2.1 单一职责原则(Single Responsibility Principle,SRP)

一个类应该只有一个职责。通过将职责抽象成不同的类或方法,我们可以更好地组织和管理代码。

所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。

很多人认为这个原则是针对一个类进行描述,在我看来这里使用模块这个更加抽象的名词更为合适。在设计中,小到一个方法,大到一个模块我们都应该尽量去遵循单一职责原则。

相信在我们日常开发中,每个人都遇到过改了一个问题经常会引起另一个问题。其原因大多都是因为在设计上违背了单一职责原则。

如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能地遵守单一职责原则。

此原则的核心就是解耦合增强内聚性,是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。

还是上面的例子:

class UserManager:
    def __init__(self, user_repo):
        self.user_repo = user_repo

    def create_user(self, name, email, password):
        if not EmailValidation.validate(email):
            # ...throw InvalidEmailException...
            pass
        if not PasswordValidation.validate(password):
            # ...throw InvalidPasswordException...
            pass
        user = User(name, email, password)
        self.user_repo.save_user(user)

    def get_user_by_email(self, email):
        if not EmailValidation.validate(email):
            # ...throw InvalidEmailException...
            pass
        user = self.user_repo.get_user_by_email(email)
        return user

class UserRepo:
    def __init__(self, db):
        self.db = db

    def save_user(self, user):
        # ...save user to db...

    def get_user_by_email(self, email):
        # ...query db to get user by email...

还是类似的例子,UserManager 类负责用户的创建和获取,而 UserRepo 类负责用户的存储和查询。这两个类各自承担了不同的职责,符合单一职责原则。如果将这两个职责合并到一个类中,就会导致这个类的职责过于复杂,难以维护。

1.2.2 开闭原则(Open-Closed Principle,OCP)

当我们在软件开发中添加新功能或修改现有功能时,我们希望这些更改不会破坏现有的代码。这就是开闭原则(Open-Closed Principle,OCP)的核心思想:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

换句话说,我们应该通过添加新的代码来扩展软件的功能,而不是修改现有的代码。这样做的好处是,我们可以保证现有的代码仍然能够正常工作,同时也可以避免引入新的错误。

以下是一个符合开闭原则的例子:

class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

在这个例子中,Shape 类定义了一个抽象方法 area,表示计算形状的面积。Rectangle 和 Circle 类继承了 Shape 类,并实现了 area 方法来计算矩形和圆形的面积。如果我们需要添加一个新的形状,例如三角形,我们只需要创建一个新的类并实现 area 方法即可,而不需要修改现有的代码。

这个例子中的代码符合开闭原则,因为它对扩展开放(我们可以添加新的形状),对修改关闭(我们不需要修改现有的代码)。

遵循开闭原则的好处是,我们可以编写具有可扩展性和可维护性的代码,从而提高软件开发的效率和质量。如果我们的代码不具备这些特性,那么在软件开发过程中就会遇到很多问题,例如:

当我们需要添加新的功能时,我们可能需要修改现有的代码,这可能会破坏现有的功能或引入新的错误。 当我们需要修改现有的代码时,我们可能会影响到其他部分的代码,这可能会导致其他功能出现问题。 当我们需要维护代码时,我们可能会发现代码难以理解或难以修改,这可能会导致维护成本增加。

遵循开闭原则是非常重要的,它可以帮助我们编写具有可扩展性和可维护性的代码,从而提高软件开发的效率和质量。

1.2.3 里氏替换原则(Liskov Substitution Principle,LSP)

里氏替换原则(Liskov Substitution Principle,LSP)是 SOLID 原则中的第三个原则,它强调了子类应该能够替换其父类并且不会影响程序的正确性。

里氏替换原则强调的是设计和实现要依赖于抽象而非具体;子类只能去扩展基类,而不是隐藏或者覆盖基类,它包含以下4层含义

子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。

以下是一个符合里氏替换原则的例子:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def get_width(self):
        return self.width

    def set_width(self, width):
        self.width = width

    def get_height(self):
        return self.height

    def set_height(self, height):
        self.height = height

    def area(self):
        return self.width * self.height

class Square(Rectangle):
    def __init__(self, size):
        self.width = size
        self.height = size

    def set_width(self, width):
        self.width = width

    def set_height(self, height):
        self.width = height

在这个例子中,Rectangle类表示一个矩形,它有一个 width 和一个 height 属性,以及一个计算面积的方法 area。Square 类继承了 Rectangle 类,并重写了 set\_width 和 set\_height 方法,以确保正方形的宽和高始终相等。

这个例子中的代码符合里氏替换原则,因为我们可以用 Square 类的实例替换 Rectangle 类的实例,并且程序的行为不会受到影响。这是因为 Square 类继承了 Rectangle 类的行为,并且没有改变它。

1.2.4 接口隔离原则(Interface Segregation Principle,ISP)

接口隔离原则也叫Interface Segregation Principle、ISP,要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。2002 年罗伯特·C·马丁给“接口隔离原则”的定义是:客户端不应该被迫依赖于它不使用的方法(Clients should not be forced to depend on methods they do not use)。

该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。

以上两个定义的含义是:要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

感觉可能与单一职责原则很像,接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

简单来说接口隔离原则与单一职责的定义的规则是不相同的,单一职责要求的是类和接口职责单一,注重的是职责,没有要求接口的方法减少,例如一个职责可能包含 10个方法,这 10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,在系统外通过文档约束不使用的方法不要访问,按照单一职责原则是允许的,按照接口隔离原则是不允许的。

1.2.5 依赖倒置原则(Dependency Inversion Principle,DIP)

依赖倒置原则(Dependency Inversion Principle,简称DIP)是面向对象设计原则之一,它强调高层模块不应该依赖于低层模块,而是应该依赖于抽象。换句话说,依赖关系应该建立在抽象层次上,而不是具体实现层次上。这样可以降低模块间的耦合度,提高系统的可扩展性和可维护性。

依赖倒置原则主要包含以下两个方面:

高层模块不应该依赖于低层模块,两者都应该依赖于抽象。 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

为了更好地理解依赖倒置原则,我们可以通过以下几个步骤来应用它:

识别模块间的依赖关系:在设计系统时,我们需要识别出各个模块之间的依赖关系,以便确定哪些模块需要遵循依赖倒置原则。

定义抽象接口:为了实现依赖倒置原则,我们需要定义一个抽象接口,让高层模块和低层模块都依赖于这个接口。这样,高层模块就不再直接依赖于低层模块的具体实现,而是依赖于抽象接口。 实现抽象接口:低层模块需要实现抽象接口,这样高层模块就可以通过抽象接口与低层模块进行交互。这种方式可以确保高层模块与低层模块之间的解耦,提高系统的可扩展性和可维护性。

依赖倒置原则是一种有效的设计原则,可以帮助我们构建更加灵活、可扩展和可维护的系统。通过将依赖关系建立在抽象层次上,我们可以降低模块间的耦合度,从而提高系统的整体质量。

1.2.6 小结

原则含义
单一职责原则 (Single Responsibility Principle,SRP)一个类应该只有一个职责。通过将职责抽象成不同的类或方法,我们可以更好地组织和管理代码。
开闭原则 (Open-Closed Principle,OCP)一个类应该对扩展开放,对修改关闭。通过使用抽象类或接口来定义通用的方法或属性,我们可以更好地实现代码的扩展性。
里氏替换原则 (Liskov Substitution Principle,LSP)子类应该能够替换父类。通过使用抽象类或接口来定义通用的方法或属性,我们可以更好地实现代码的可扩展性和可维护性。
接口隔离原则 (Interface Segregation Principle,ISP)一个类不应该依赖于它不需要的接口。通过将接口抽象成不同的类或方法,我们可以更好地组织和管理代码。
依赖倒置原则 (Dependency Inversion Principle,DIP)高层模块不应该依赖于低层模块,它们应该依赖于抽象。通过使用抽象类或接口来定义通用的方法或属性,我们可以更好地实现代码的可扩展性和可维护性。

1.3 应用设计模式

设计模式是一种常见的代码设计思想,它可以帮助我们解决常见的代码设计问题。通过使用抽象思维,我们可以更好地理解和应用设计模式,以便更好地组织和管理代码。

抽象思维在代码设计中非常重要。通过使用不同的代码设计思想和准则,我们可以将具体的事物抽象成一般性的概念或模型,以便更好地组织和管理代码。一堆抽象度很低的代码,很容易就会成为我们项目当中人人唾弃的对象(简称代码“屎山”)。

这里总结了23种常见的设计模式,在做系统设计时,可以根据需求,使用对应的设计模式:

类别设计模式简要介绍
创建型模式单例模式确保一个类只有一个实例,并提供一个全局访问点。
工厂方法模式定义一个创建对象的接口,让子类决定实例化哪一个类。
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
建造者模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
原型模式通过复制现有的实例来创建新的实例。
结构型模式适配器模式将一个类的接口转换成客户期望的另一个接口。
桥接模式将抽象部分与实现部分分离,使它们可以独立地变化。
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。
装饰器模式动态地给一个对象添加一些额外的职责。
外观模式为子系统中的一组接口提供一个统一的接口。
享元模式使用共享技术有效地支持大量细粒度的对象。
代理模式为其他对象提供一个代理以控制对这个对象的访问。
行为型模式责任链模式为请求创建一个接收者对象的链。
命令模式将一个请求封装为一个对象,从而使您可以用不同的请求对客户进行参数化。
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器。
迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
中介者模式用一个中介对象来封装一系列的对象交互。
备忘录模式在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
观察者模式当一个对象的状态发生改变时,其相关依赖对象会被自动更新。
状态模式允许一个对象在其内部状态改变时改变它的行为。
策略模式定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。
模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
访问者模式在不改变数据结构的前提下,定义作用于某种数据结构中的各元素的新操作。

02、高可用设计

2.1 设计高可用的系统架构

高可用的系统架构是实现系统高可用性的基础。因此,我们应该设计高可用的系统架构,包括负载均衡、集群、分布式、异地多活等特点。例如,我们可以使用腾讯云CLB来实现负载均衡,使用Kubernetes来实现容器集群,使用分布式数据库来实现数据的高可用性,使用异地多活来实现系统的容灾和备份。

在系统设计之初,一定要避免出现服务单点问题!

2.2 日志和监控,包含服务级和业务级

监控是保证系统高可用性的重要手段。我们应该做好各路的监控,包括系统的性能监控、服务的健康监控、网络的流量监控、日志的收集和分析等。通过监控,我们可以及时发现系统的异常和故障,快速定位和解决问题,保证系统的稳定性和可用性。

在做日志和监控设计时,应该考虑以下因素:

可读性:日志和监控应该易于阅读和理解,以便快速定位和解决问题。 可靠性:日志和监控应该具有高可靠性,以确保监控结果的准确性。 可扩展性:日志和监控应该能够轻松地扩展,以满足未来的需求。 实时性:日志和监控应该具有实时性,以便快速发现和解决问题。

2.3 做好容灾设计

容灾设计对于一个系统的稳定性来说至关重要。从系统和架构设计的角度,做好服务容灾需要综合考虑多个方面,包括分布式架构、多数据中心冗余、数据备份与恢复、容灾预案、监控告警、容量规划等。通过这些措施,可以提高系统的可用性和容错能力,确保在发生灾难时能够快速恢复服务。

对于部分系统,在发生故障时,选择性地进行服务降级,也是一种有效的手段:牺牲掉不重要的服务,保证核心服务的正常运行。

下面有一些容灾设计的准则,供大家参考:

分布式架构:采用分布式架构可以提高系统的可用性和容错能力。通过将服务拆分成多个微服务,并部署在不同的服务器上,可以降低单点故障的风险。同时,分布式架构还可以提高系统的伸缩性,以应对不同的业务需求。 多数据中心和地域冗余:为了应对自然灾害或其他不可抗力因素,可以将服务部署在多个数据中心和地域。这样,即使某个数据中心出现故障,其他数据中心仍可以继续提供服务。同时,通过使用负载均衡和智能路由技术,可以确保用户请求被分配到最近的可用数据中心。 数据备份和恢复:定期对关键数据进行备份,并将备份数据存储在安全的地方,以防数据丢失。同时,需要制定数据恢复计划,并定期进行恢复演练,以确保在发生灾难时能够快速恢复数据。 容灾预案:制定详细的容灾预案,包括灾难发生时的应急响应流程、责任人、通讯方式等。并定期组织演练,以提高团队在应对灾难时的协同能力。 监控和告警:建立全面的监控体系,对系统的各个环节进行实时监控,包括服务器、网络、数据库、应用等。一旦发现异常,立即触发告警,通知相关人员进行处理。 容量规划:根据业务发展趋势和历史数据,进行容量规划,确保系统具备足够的资源来应对未来的业务增长。同时,需要定期进行容量评估,以便及时调整资源分配。

2.4 系统稳定是一名后台开发的生命线

稳定是后台开发的生命线。我们应该注重系统的稳定性和可靠性,从设计、开发、测试、部署、运维等各个环节,都要注重稳定性和可靠性验证。我们应该遵循“稳定优先”的原则,保证系统的稳定性和可用性,为用户提供更好的服务和体验。

此外,腾讯云开发者公众号也分享过不少关于高可用系统的建设思路和经验如工作十年,在腾讯沉淀的高可用系统架构设计经验腾讯专家10年沉淀:后海量时代的架构设计

03、想尽一切办法“偷懒”

这里说的“偷懒”并不是让大家真的去偷懒,而是一种思考问题的方式。

打个比方,产品经理经常会要求我们导出近期一段时间内,用户的活跃数据、用户的付费意愿、域名的带宽增长情况、某个新上功能的使用情况等,每次的要求都有所不同,非常耗费和占用研发的时间。后面我们想了个办法,我们将这些运营基础数据(用户纬度、域名纬度、功能纬度),通通上报到公司的BI系统(类似于一个运营报表生成平台),让产品自行决策需要哪些数据、怎么使用这些数据,这样研发既不需要再给产品经理导出数据,又可以让产品经理自己随意“折腾”,双方的效率都得到了很大的提升。

下面这些方法论可以供给大家参考。

3.1 优化重复流程、一切自动化

在工作中,我们经常需要处理重复的流程,例如手动导出数据、手动处理数据等等。这些重复的流程不仅浪费时间和精力,还容易引入错误和漏洞。建议定期复盘近期一直在重复做的事情,主动优化掉这些重复的工作内容。

3.2 使用模板和标准化流程

模板和标准化流程是另一种避免重复流程的方法。例如,我们可以使用标准化流程来处理数据,使用模板来生成重复的文档或报告等等。举个例子,我们需要每周向客户发送一份报告,将每周的运营数据和相关情况同步给客户。其实我们完全可以创建了一个标准化的报告模板,并使用自动化脚本来生成报告,这样大大减少了手动编写报告的工作量,提高了工作效率。

3.3 应用项目管理工具和流程

项目管理工具和流程是一种常见的管理方法,它们可以帮助我们优化重复流程,提高工作效率。例如,我们可以使用项目管理工具来跟踪任务和进度,使用流程图来优化流程等等。

04、从价值出发,专注于最重要的事

我们应该始终坚持从用户价值和需求价值出发,关注用户的需求和痛点,为用户提供有价值的产品和服务。同时,我们也应该多问一些为什么,思考这件事是否是当下最重要的事情,这个事情不做或怎么样,当前我最重要的是做什么。只有选对了方法和方向,才能做出有意义的事情,为用户和整个产品创造价值。

在我过去的经验里面,很多时候前方接触客户的同学经常会叫着说“XX客户要求实现一个怎么怎么样的需求,我们赶紧搞起来”,但往往这个时候,我会反问一句:“这个事情不做会怎么样?做了又能带来什么收益?我们是不是应该从整个系统设计的角度,提前就满足好这类客户的需求,而不是临时抱佛脚?”。其实我们会发现,往往就因为这么多问一句,会发现其实这个需求并没有那么重要,更重要的是我们需要提炼出更多通用化系统化的能力,提前满足到这类用户/客户的场景,对于我们整个系统和产品的长远发展来说显得尤为重要。

如果我们总是在干一些快速和短期的事情,就需要思考我们是否真的在朝着我们的目标在进发了。

05、沟通与协作也是身为开发的必备技能

在软件开发中,沟通能力是非常重要的技能。很多开发同学会直接把自己简单纯粹地定义为“一个只负责写代码的人”,这其实是一个思想上的误区。在开发过程中,程序员其实需要与团队成员如产品经理,甚至用户或客户进行大量的沟通,以便更好地理解需求、协同工作、解决问题等。

  • 更好地理解需求

程序员需要与产品经理,甚至用户进行沟通,以便更好地理解需求。只有理解了用户和客户的需求,才能开发出符合需求的软件产品。如果程序员没有良好的沟通能力,就很难理解用户和客户的需求,从而导致开发出的软件产品与用户和客户的需求不符。

  • 协同工作

稍微大点的项目开发,往往是一场“团战”。程序员需要与其他同事进行沟通,以便更好地协同。只有通过良好的沟通,团队成员才能理解彼此的需求和想法,协同完成项目的开发和交付。如果程序员没有良好的沟通能力,就很难与团队成员进行有效的沟通和协作,从而导致项目的延误和质量问题。

  • 解决问题

在软件开发中,问题是难以避免的。程序员需要与团队成员、用户、客户等进行沟通,以便更好地解决问题。只有通过良好的沟通,才能及时发现和解决问题,保证项目的顺利进行。如果程序员没有良好的沟通能力,就很难与团队成员、用户、客户等进行有效的沟通和问题解决,从而导致问题的滋生和扩大。

  • 推动和解决

在大公司里面,推动沟通能力也是一种掌控资源的能力,我们往往会发现,沟通不好的人推动事情总是很慢,总是获得不到他想要的资源,导致事情一直难以获得落地。

打个比方,TEAM A 的小 X 是一名程序员,虽然技术能力很强但不善言辞,他在项目上有一个获取 IP 地理位置的需求,刚好另一个团队 TEAM B 有这块的一些积累,小 X 希望能调用他们的接口,获取到 IP 的一些地理位置信息。但奈何 TEAM B 的同学对这个事情提不起兴趣,觉得这不是他们的义务,也没什么收益,所以不管小 X 怎么努力,TEAM B 的人就是不予理睬,不配合。这时候小 X 的一位同事小 Y 发现了这个问题,并协助他一块解决,小 Y 是怎么做的呢,首先小 Y 拉上了 TEAM A 和 TEAM B 团队的领导,并说明了这个事情的重要性、对公司的意义、对双方团队的收益,很快,TEAM B 的领导马上就安排了一名团队核心骨干同学参与支持,并解决了问题,小 X 对小 Y 佩服得五体投地。

所以,沟通能力对于程序员来说也非常重要,千万别小看沟通这项技能,毕竟身为程序员,除了写代码以外,还有很长的时间都在和人在打交道。

06、凡事有交代,件件有着落,事事有回音

“凡事有交代,件件有着落,事事有回音”,其实翻译过来就是“做事靠谱”,负责的事情一定会认真负责到底,及时反馈和响应。

成为一位靠谱同事,你的行为准则就是你个人的铭牌,同事会十分愿意与你合作。职场本质上也是一个互相支持的过程,你对别人工作的支持,在你有求时别人也会积极支持你。

成为一位靠谱的员工,上司会非常喜欢这类员工,工作认真负责,交办的事可控。你的行事风格,会让上司看在眼里,记在心里,在上司打考核的时候也会考虑到这一点。

遵循“凡事有交代,件件有着落,事事有回音”原则,能够更好地完成自己的工作任务,提大家整体的协作效率,在我看来也是非常重要的一项行为准则。

07、保持开放和学习的心态

在当前技术更新迭代速度非常快的时代,程序员需要时常保持有一颗学习的心态。

  • 技术更新迭代速度非常快

当前的技术更新迭代速度非常快,新技术和新工具层出不穷。如果程序员不保持开放和学习的心态,就很容易被淘汰,无法适应新的技术,从而影响长期的职业发展。

  • 学习是程序员的必备技能

作为程序员,学习是必备的技能。只有不断学习新的技术和工具,才能保持自己的竞争力和创新能力。比如最近火爆全网的chatgpt,如果不及时跟进学习类似的AI工具,相信很快就会有不少的人被淘汰掉。

  • 学习可以提高工作效率和质量

学习新的技术和工具可以提高程序员的工作效率和质量。新的技术和工具通常都有更高效的解决方案和更好的性能,可以帮助程序员更快地完成工作任务,提高工作效率和质量。 比如GO语言在1.18版本新推出的泛型支持,可以很好地解决GO中缺乏泛型,导致一个相同功能的函数,需要为不同的变量类型重复写很多遍函数的问题。

总体来说,程序员我认为是一个需要保持终身学习的职业,知识永远都在往前更新迭代,我们需要做的,就是保持一颗开放和学习的心态,对技术保持敏感和热度。

08、职业发展

职场中难免遇到不好的团队、不好的项目,这种时候该怎么办?从三个角度出发:

· 是否工作得开心。

· 在这里工作是否能持续得到自我提升和自我满足。

· 金钱方面是否能够满足。

如果三个里面有两个都是“NO”,那么你可能需要考虑一下换份工作了,换一个更广阔的平台,找到一条合适自己的道路。每个人都是一个自由而独立的个体,我们大可不必吊死在一棵树上。

09、横向发展

作为一名开发,往往会将自己的视野仅仅聚焦在手头在做的事情上面,忽略了自己身份是一个职业人的事实。因此,多做“横向发展”非常重要:

  • 增强职业竞争力

除了技术能力之外,软性技能也非常重要。例如沟通能力、团队合作能力、领导力等,都可以帮助我们更好地完成工作任务,提高工作效率和质量。如果程序员只关注技术能力,而忽略了软性技能,就会在职业竞争中处于劣势,毕竟我们的工作内容可不只有敲代码这件事。

  • 拓宽职业发展道路

多元化发展可以拓宽职业发展道路。程序员可以通过学习其他领域的知识和技能,拓宽自己的职业发展道路。例如,学习产品设计、市场营销等知识,可以帮助我们更好地理解客户需求和市场趋势,从而更好地完成工作任务,实现职业发展。

  • 未来的选择更多样化

多做一些其他领域的知识储备,可以在我们未来无法继续胜任开发工作的时候,有更多的职业选择。拥有技术背景的我们,转到其他岗位上也会有如虎添翼的感觉。

以上就是本篇文章的全部内容啦,如果觉得内容有用,欢迎转发收藏\~

聊一聊令你受益的职场思维(点这里进入开发者社区,右边扫码即可进入公众号)。我们将选取1则最有创意的分享,送出腾讯云开发者-马克杯1个(见下图)。5月31日中午12点开奖。

让AI替你打工?GPT提升开发效率指南.png

推文-长-2.png

10人面试9个答错?鹅厂T12详解MySQL加锁机制.png


腾讯云开发者
21.9k 声望17.3k 粉丝