`

程序员构建总是出问题,怎么办?

阅读更多

​构建这一问题,到底是哪个环节出了 Bug?




我总是听到程序员谈论构建的问题:“构建出错了”,“我把构建搞坏了”等等。然而,真正的问题在于构建这个概念本身就有问题。每次修改应用程序都需要从头重新构建的想法从根本上就有缺陷。

这个概念的实际问题在于,构建会导致开发过程中的反馈循环漫长而痛苦。有些系统的应对手段是通过极快的速度,他们的观点是,哪怕你每次进行修改都编译整个系统,也不是问题,因为在你反应过来之前构建就会结束。

问题在于,系统的规模会一天天扩大,终有一天这个法子再也行不通。

更深层次的问题是,你会发现每次修改代码后,即使立即重新构建,还是需要重新启动应用程序,而且每当你发现一个问题、修改代码、经过重新编译后,问题还是没有消失。换句话说,构建与实时编程是对立的。构建的反馈循环永远都那么漫长。

从根本上讲,每次修改代码的时候,没必要重新编译整个系统。就好像你想换个灯泡也没必要把整座摩天大楼都拆了重建吧。

无论怎么优化,构建都无法真正成为实时。

因此,诸如make之类的工具永远也无法解决问题。而且这些工具本身还有许多其他问题。例如,我们必须复制代码中嵌入的依赖项信息:import、include、use或 extern 声明之类的语句为我们提供的文件和模块信息与我们手动输入并无二样。这些复制工作乏味且容易出错。而且这种复制太粗糙,粒度仅限于文件。编译器可以更精确地管理这些依赖关系,例如跟踪何处使用了哪些函数。

注意:有些工具(如GN)可以由编译器提供依赖关系。虽然仍然很粗糙。

另外,这些工具提供的语言所具备的抽象机制和工具支持都很差。一般大家对make的不满是其引入了其他类似性质的工具层,例如Cmake。

一个更好的解决方案是,为构建生成更好的DSL。基于某种真正的编程语言的内部DSL是改善问题的一种方法。例如 rake 和 scons,分别使用了 Ruby 和 Python。这些工具简化了构建工作,但它们仍与构建有着千丝万缕的联系,这才是我最关心的根本问题。

话虽如此,如果我们不打算使用传统的构建系统来管理我们的依赖项,那么应该怎么办?

首先,我们需要认识到我们的许多依赖关系不是基础的东西,例如可执行文件、共享库、目标文件和任意类型的二进制文件等。我们真正需要“构建”的只有源代码。毕竟,如果使用解释器,那么你只需创建最基本的源代码,然后逐步编辑和发展源代码。

使用解释器可以避免构建二进制文件的问题。




成本是性能。编译是一种优化,尽管是很重要的优化,通常是必不可少的。编译需要比解释器更全面的分析,而且还会执行预先计算,以避免我们在执行过程中重复工作。从某种意义上说,编译器的作用是记住解释器的部分工作。

许多动态JIT正是实现了这一点,但从根本上讲,静态编译也是如此——你只需提前记住即可。

从这个角度看,构建是分阶段执行的一种形式,而我们不断构建的二进制文件只是缓存。

我们可以通过结合解释与编译,解决解释器的性能难题。许多使用JIT编译器的系统正是这样做的。其优点之一在于,我们不必在启动应用程序之前等待优化。还有我们可以修改代码,并通过重新解释和重新优化立即反应修改的结果。

然而,并非所有的JIT都会这样做,但是这种做法已经延续了数十年,例如Smalltalk VM等。Smalltalk 有很多优点,其中之一便是你很少会遇到构建的麻烦。

然而,即使假设你的JIT引擎在发展的过程中对代码进行了增量优化,你与实时开发之间仍有障碍,这个障碍就是你仍然需要构建。

类型。如果你的代码由于类型错误而出现问题,该怎么办?再次重申,我们不需要通过构建来检测到这一点。增量式类型检查器能够在保存代码时发现问题。当然,以往我们很少使用增量式类型检查器。实时系统的开发历来采用动态类型语言并非巧合。但是,没有根本性的原因能够说明为什么我们不能使用静态类语言支持增量开发。这些技术可以追溯到Cecil。有一篇有关Scala.js的文章https://2016.splashcon.org/details/splash-2016-oopsla/36/Parallel-Incremental-Whole-Program-Optimizations-for-Scala-js详细讨论了使用静态类语言进行增量编译。

测试。很多时候,构建过程包含测试,而构建失败也是由于测试发现了应用程序中的逻辑错误。但是,测试本身并不是构建的一部分,也不必依赖于构建,毕竟构建只是更新应用程序的一种方法。在实时系统中,源代码会立即反映到更新后的应用程序中。在这种环境中,测试可以针对每次更新展开,但是开发人员无需等待测试完成。

资源。应用程序可以结合各种资源:媒体、文档、各种数据(源文件或二进制文件、表或机器学习模型等)。其中一些资源可能需要自己计算,例如利用TeX或markdown之类的文档生成PDF或HTML,而且很少包含实时或增量的阶段。

即便我们的资源可供随时使用,过度依赖文件系统结构也会引发问题。资源通常会以文件的形式呈现。部署的结构可能与源代码库不同。在源代码库中编辑组件不会改变构建结构。

纠正这些问题并非易事,通常软件工程师甚至都不愿意尝试。相反,他们会越来越依赖构建过程。这真的没有必要。

我们可以将资源视为缓存的对象,并根据需要生成它们。部署应用程序时,我们需要确保所有资源都经过了预先计算,而且缓存在应用程序的固定位置上,如果在开发过程中这个缓存丢失,那么应用程序还能将它们放回同一个位置。软件知道这些位置,因此它能够找出缓存在应用程序固定位置上的资源。

当通过应用程序逻辑访问资源时,上述推理过程就很合理。那么那些没有被应用程序使用,而是提供给用户使用的资源呢?在有些情况下,文档、示例代码以及附带的资源都属于这种情况。这类资源的处理不是应用程序本身的一部分,因此,它不是构建问题,而是部署问题。也就是说,部署只是将合适的对象放到既定的位置上。

处理多种语言。在处理多种语言时,由于某些语言不支持增量开发,所以我们可能会被迫使用构建系统。假设应用程序的核心是用实时语言编写的,那么我们应该将其他语言视为资源;它们的二进制文件是开发过程中动态计算并缓存的资源。



总结



构建和实时是针锋相对的。

编译结果是缓存资源的一种形式,它是分阶段执行的结果。

为了在工业环境中实现实时,我们需要通过构建开发环境,确保每个阶段都经过了严格的优化。

当底层的缓存值过期时,应自动缓存分段结果并无效化旧的缓存值。

无论分阶段保存的值是资源、共享库/二进制文件还是其他内容,这种方法统统适用。

对于计算缓存值和确定缓存有效性来说,必要的数据必须保存在应用程序的固定位置。

让我们建设一个全新、勇敢、没有构建的世界。



分享到:
评论

相关推荐

    浅谈数据库设计技巧-程序员应该读的

    通过学习和实践这些数据库设计技巧,程序员能够更好地应对各种复杂的业务场景,构建出高效、可靠的数据存储系统。在实际工作中,不断总结经验,持续学习新的数据库技术和最佳实践,是保持竞争力的关键。

    知乎:「只差程序员」为什么会招黑?

    Shiro提供了认证、授权、会话管理和加密等功能,帮助程序员构建安全的应用。而Spring是Java领域广泛应用的开源框架,它简化了企业级应用的开发,包括依赖注入、事务管理等。 此外,程序员还需要具备良好的编程习惯...

    ChatGPT火遍全球, 为啥程序员还要继续学习C#?

    尽管ChatGPT等自然语言处理工具在问答、代码生成等方面展现出强大能力,但它们并不能完全替代人类程序员。ChatGPT虽然可以理解自然语言并生成代码,但在复杂项目管理、需求分析、系统架构设计等方面,人类的创造力和...

    【秘籍】程序员喜欢什么样的产品经理?.docx

    在IT行业中,程序员与产品经理的合作关系至关重要,因为两者都是构建成功产品的关键角色。然而,由于他们的专业背景和思维方式不同,可能会产生冲突和误解。要建立良好的协作关系,产品经理需要了解程序员的需求和...

    惹恼程序员的十件事 IT人事必看

    7. **测试与调试**:程序员经常需要花费大量时间进行测试和调试,找出并修复代码中的错误。这是一个繁琐且需要耐心的过程,尤其是在面对复杂问题时,可能会导致情绪低落。 8. **代码审查**:代码审查是保证代码质量...

    像程序员一样思考

    编程的真正挑战不是学习一种语言的语法,而是学习创造性地解决问题,从而构建美妙的应用。《像程序员一样思考》分析了程序员解决问题的方法,并且教授你其他图书所忽略的一种能力,即如何像程序员一样思考。全书分为...

    程序员教程(pdf)

    理解这些概念有助于提升你的问题解决能力,设计出更高效的代码。 此外,教材还将涵盖面向对象编程(OOP)的概念,包括类、对象、继承、多态和封装等。这些是许多现代编程语言的基础,如Java、C++和Python。通过学习...

    一本关于程序员面试的书籍,希望大家喜欢

    这本书涵盖了编程基础、数据结构、算法、操作系统、计算机网络、数据库等多个核心领域,为读者构建了一个全面的面试知识体系。 在编程基础部分,书中详细介绍了各种编程语言的关键概念和语法特性,包括但不限于Java...

    像程序员一样思考.pdf

    编程的真正挑战不是学习一种语言的语法,而是学习创造性地解决问题,从而构建美妙的应用。《像程序员一样思考》分析了程序员解决问题的方法,并且教授你其他图书所忽略的一种能力,即如何像程序员一样思考。全书分为...

    程序员的必备工具

    此外,代码质量和性能分析工具,如SonarQube、JProfiler,可以帮助找出潜在的问题和性能瓶颈。版本控制工具,如Git,对于协作开发至关重要。 最后,持续集成/持续部署(CI/CD)工具,如Jenkins和Travis CI,自动化...

    程序员必读书推荐

    书中不仅介绍了C语言,还能够教会你像程序员一样思考问题,理解底层架构的运作机制。 《算法导论》则是那些希望成为算法大师的程序员的必备书籍。它不仅教你如何编写代码,更重要的是,它教会你如何创建和实现新...

    程序员必备算法知识

    在IT行业中,算法是程序员解决问题的关键工具,它们是编程的基础,能够帮助我们高效地处理数据和执行任务。本文档集合中的四个PHP文档深入探讨了程序员应掌握的一些经典算法,这对于提升编程技能至关重要。 首先,...

    java程序员的成长历程

    理解线程的基本概念,如同步、互斥、死锁,以及Java提供的线程工具,如synchronized关键字、wait/notify机制、ThreadLocal和ExecutorService,将使程序员能够处理复杂的并发问题。 Java程序员还应掌握异常处理,...

    java程序员面试简历 WORD 模版

    首先,标题"java程序员面试简历 WORD 模版"表明这是一个专门为Java程序员设计的Word文档模板,用于构建面试简历。在制作简历时,你需要关注以下几点: 1. **个人信息**:简历的开头应包含姓名、联系方式(电话、...

    《程序员教程》 电子书下载

    《程序员教程》是一本专为编程初学者和有经验的开发者设计的电子书,旨在帮助读者深入理解编程概念,提高编程技能,以及掌握如何从数据中提取价值的关键知识。本书涵盖了数据挖掘、商业智能、商业分析和行业应用等多...

    程序员数学逻辑数学

    逻辑数学对于程序员和机器学习初学者来说尤为重要,因为它能够帮助他们更清晰地构建逻辑结构,提高代码的严谨性和效率。 #### 3. 数制转换和计数法 在计算机科学中,理解不同的数制转换和计数法是十分重要的。例如...

    CMake_ 工程构建工具-为程序员服务.外部构建1

    【CMake:工程构建工具——为程序员服务】 CMake 是一个跨平台的自动化构建系统,它简化了在不同操作系统上构建软件的过程。相比于传统的 Unix-like 系统中使用的 autotools 和 GNU Make,CMake 提供了一种更统一且...

    像程序员一样思考pdf

    它深入浅出地介绍了如何运用程序员的思维方式来解决日常生活和工作中遇到的问题,旨在培养逻辑思维、分析能力和抽象思维技巧。 书中首先阐述了程序员思维的基础——逻辑与推理。逻辑是编程的核心,学习如何构建有效...

Global site tag (gtag.js) - Google Analytics