Skip to content

2020

解釋 Angular 錯誤:'Expression Changed After It Has Been Checked'

我的一位同事在開發 Angular 前端應用程序時遇到了一個錯誤訊息,錯誤訊息為:

ExpressionChangedAfterItHasBeenCheckedError:Expression在檢查之後已更改。先前的值:'null',當前的值:'true'。

這個錯誤發生在他開發一個從第二頁導航回第一頁的後退按鈕功能時。第一頁已經渲染過一次,並需要以不同的初始值重新渲染。

這個錯誤的根本原因在於Angular的變更檢測機制。每次操作後,Angular都會將用於該操作的值存儲在組件視圖的 oldValues 屬性中。一旦所有組件都已經檢查過,Angular就會啟動下一個摘要週期。但是,它不進行操作,而是將當前值與上一個週期存儲的值進行比較。

值得注意的是,這種額外的檢查只在開發模式中發生。Angular從組件樹的頂部到底部強制執行單向數據流。一旦父組件的變更已經被處理,就不允許任何子組件更新父組件的屬性。

要解決上述問題,可能的解決方案包括使用異步更新,如 setTimeout,或者在 ngAfterViewInit() 的生命週期鉤子中手動觸發變更檢測,利用 _changeDetectorRef.detectChanges()ChangeDetectorRef 類提供以下五種方法:

抽象類 ChangeDetectorRef {
 抽象 markForCheck(): void;
 抽象 detach(): void;
 抽象 detectChanges(): void;
 抽象 checkNoChanges(): void;
 抽象 reattach(): void;
}

通過利用這些方法,您可以手動運行變更檢測並更新子視圖。我的同事很高興發現錯誤在這種解釋之後得到了解決。

Writing Unit Test Cases with Karma for Angular Components

I'd like to make the case for writing unit tests for your Angular web app. Accelerating time to production is not a valid excuse for accumulating technical debt. Here are some compelling reasons to start:

  1. Unit tests help identify issues as early as possible, especially when multiple teams are working on the same codebase, inadvertently introducing bugs. Avoiding middle-of-the-night calls for production support is a worthy goal.

  2. Tests enable you to refactor your code confidently, ensuring that your app continues to function as expected. You can divide your code into manageable, testable units, as opposed to dealing with a monolithic system.

  3. Your company's policy may mandate a certain level of code coverage, often 80% or higher.

If you're new to this, you may not know how to get started or why it's important. Fortunately, Angular makes it easy. To begin, simply run the following command in your project directory:

npm run test

This will open a Chrome browser window at localhost, on port 9876.

Image 1

Click the "Debug" button to initiate testing.

Image 2

At this point, no tests will run because we haven't written any yet. But you can start writing test cases to cover specific, isolated pieces of code. For instance, let's consider a login.component.ts file, which contains a login() method that toggles a boolean flag from false to true:

export class LoginComponent {
  isLogon = false

  login() {
    this.isLogon = true
  }
}

Next, create a file named login.component.spec.ts for your test cases. Write your first test case as follows:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

Inside your describe() function, you'll find the test cases. Each case is within its own it() function. The aim here is to test if the isLogon flag turns true after the login() method is triggered.

Image 3

Your first test case should pass! If another developer alters your code, your test will catch it:

Image 4

In a real-world scenario, you might make an API call to a server. However, it's crucial not to call the actual API during your test. Instead, you should mock your API call with stub data.

For instance, let's enhance our LoginComponent to make a service call:

import { AuthenticationService } from "../../services/authentication.service"

export class LoginComponent {
  constructor(private authenticationService: AuthenticationService) {}

  isLogon = false

  login() {
    this.authenticationService.login().subscribe(
      data => {
        this.isLogon = true
      },
      error => {
        this.isLogon = false
      }
    )
  }
}

Now your test will fail because the AuthenticationService isn't yet injected into our testing environment. We can fix this as shown below:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"
import { AuthenticationService } from "../../services/authentication.service"
import { of } from "rxjs"

const stubData = {
  username: "testing",
}

class FakeAuthenticationService {
  login() {
    return of(stubData)
  }
}

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>
  const newFakeAuthenticationService = new FakeAuthenticationService()

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [
        {
          provide: AuthenticationService,
          useValue: newFakeAuthenticationService,
        },
      ],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

Your test case should now pass!

Image 5

This example is simplified for demonstration purposes, but the key takeaway is that you should not shy away from writing unit tests.

Writing Unit Test Cases with Karma for Angular Components

Welcome to Continuous Improvement, the podcast where we explore ways to enhance your software development practices. I'm your host, Victor, and in today's episode, we'll be discussing the importance of writing unit tests for your Angular web app.

Many developers often rush to release their code as quickly as possible, sacrificing proper testing in the process. But today, we'll make a strong case for why writing unit tests should be an integral part of your development process, even if it means taking a bit more time.

Let's dive right in. The first compelling reason to write unit tests is their ability to identify issues as early as possible. When multiple teams are working on the same codebase, it's not uncommon for bugs to inadvertently find their way into the app. By having comprehensive unit tests in place, these bugs can be caught early on, preventing those dreaded middle-of-the-night support calls.

Additionally, unit tests enable you to refactor your code with confidence. Instead of dealing with a monolithic system, you can divide your code into manageable, testable units. This modular approach makes it easier to maintain and enhance your app over time.

Now, some companies have policies in place that mandate a certain level of code coverage, often 80% or higher. Writing unit tests not only helps you meet these requirements, but it also provides assurance that your code is well-tested and reliable.

If you're new to unit testing, have no fear. Angular makes it easy to get started. Simply run the following command in your project directory:

npm run test

This will open a Chrome browser window at localhost, on port 9876. From there, you can initiate testing by clicking the "Debug" button.

Now, let's discuss how to write a unit test for a specific piece of code. Imagine you have a login.component.ts file with a login() method that toggles a boolean flag from false to true. To test this functionality, create a file named login.component.spec.ts and write your test cases.

Inside the describe() function, you'll find the test cases. Each case is within its own it() function. For example, in our case, we want to test if the isLogon flag turns true after the login() method is triggered.

By running the tests, you'll see that the first test case passes successfully, ensuring that our code functions as expected. Furthermore, if another developer makes changes that impact this functionality, the test will catch it.

It's important to note that when making API calls during testing, you should avoid calling the actual API. Instead, mock your API call with stub data. This ensures that your tests run consistently, regardless of the state of the actual API.

Remember, unit testing is all about creating reliable, independent tests for individual units of code. By following this practice, you can have confidence in the functionality of your app and prevent regressions during the development process.

Well, that's a wrap for today's episode of Continuous Improvement. I hope you've gained some insight into the significance of writing unit tests for your Angular web app. Remember, taking the extra time to write tests will pay off in the long run, ensuring a more robust and maintainable codebase.

If you enjoyed this episode, make sure to subscribe to our podcast for more valuable insights on software development best practices. Until next time, I'm Victor, signing off. Happy coding!

使用Karma為Angular組件撰寫單元測試案例

我想為你的Angular網頁應用程序編寫單元測試。加速生產時間並不能成為積累技術債的合理理由。以下是一些開始的有力理由:

  1. 單元測試幫助盡可能早地識別問題,尤其是當多個團隊在同一個代碼庫上工作,不經意地引入了錯誤。避免在深夜接到生產支持的電話是一個值得追求的目標。

  2. 測試使您能夠自信地重構代碼,確保您的應用程序繼續按預期運行。您可以將您的代碼分割為可管理的,可測試的單元,而不是處理一個龐大的系統。

  3. 您公司的政策可能要求某種程度的代碼覆蓋率,通常為80%或更高。

如果你是新手,你可能不知道如何開始,或者為什麼這個重要。幸運的是,Angular讓它變得很容易。開始,只需在您的項目目錄中運行以下命令:

npm run test

這將在本地主機的9876端口打開一個Chrome瀏覽器窗口。

Image 1

點擊"Debug"按鈕以啟動測試。

Image 2

此時,由於我們還沒有寫任何測試,所以不會有任何測試運行。但是,您可以開始撰寫測試用例以覆蓋特定的,獨立的代碼片段。例如,讓我們考慮一個login.component.ts檔案,其中包含一個login()方法,該方法將一個布爾標誌從假轉為真:

export class LoginComponent {
  isLogon = false

  login() {
    this.isLogon = true
  }
}

接下來,創建一個名為login.component.spec.ts的檔案以撰寫您的測試用例。如下所示撰寫您的第一個測試用例:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

在您的describe()函式中,您將找到測試用例。每個案例都在其自己的it()函式中。這裡的目標是測試isLogon標誌在觸發login()方法後是否變為真。

Image 3

您的第一個測試用例應該通過!如果另一個開發者修改了您的代碼,您的測試將捕獲它:

Image 4

在現實世界的情境中,您可能會向服務器發送API呼叫。但是,在您的測試期間重要的是不要真的撥打API。您應該用stub數據模擬您的API呼叫。

例如,讓我們加強我們的LoginComponent以進行服務呼叫:

import { AuthenticationService } from "../../services/authentication.service"

export class LoginComponent {
  constructor(private authenticationService: AuthenticationService) {}

  isLogon = false

  login() {
    this.authenticationService.login().subscribe(
      data => {
        this.isLogon = true
      },
      error => {
        this.isLogon = false
      }
    )
  }
}

現在,您的測試將會失敗,因為AuthenticationService尚未注入我們的測試環境。我們可以如下所示修復這個問題:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"
import { AuthenticationService } from "../../services/authentication.service"
import { of } from "rxjs"

const stubData = {
  username: "testing",
}

class FakeAuthenticationService {
  login() {
    return of(stubData)
  }
}

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>
  const newFakeAuthenticationService = new FakeAuthenticationService()

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [
        {
          provide: AuthenticationService,
          useValue: newFakeAuthenticationService,
        },
      ],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

您的測試用例現在應該通過!

Image 5

這個例子為了演示的目的而簡化,但是重要的要取的是,你不應該避免撰寫單元測試。

Set Up a Django Server with Apache Virtual Host and Python Virtual Environment

It took me some time to get everything working together, so I'd like to document the steps to save you time in the future.

Firstly, assume that you already have your CentOS/Ubuntu instance running and Python installed. Create a folder for your project and set the appropriate permissions:

    sudo mkdir /opt/yourpath/projects
    sudo chown $USER /opt/yourpath/projects

If you haven't already initialized your project, you can do so with:

    python -m pip install Django
    django-admin startproject APPNAME /opt/yourpath/projects/APPNAME

By default, the server runs on port 8000:

    python manage.py runserver

To prepare your Django server for production, edit the settings.py file with the following settings:

    DEBUG = False
    ALLOWED_HOSTS = ['*']
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Then, you can build the static files using:

    python manage.py collectstatic --noinput

Next, serve your web application via the Apache web server. Assuming you installed Apache2 through yum or apt-get, enable virtual hosts for your project and create the following file:

    touch /opt/yourpath/apache2/conf/vhosts/project-vhost.conf

With the content below:

    <IfDefine !IS_APPNAME_LOADED>
      Define IS_APPNAME_LOADED
      WSGIDaemonProcess APPNAME python-home=/opt/yourpath/python python-path=/opt/yourpath/projects/APPNAME
    </IfDefine>
    <VirtualHost 127.0.0.1:80 _default_:80>
    ...

Remember to replace all instances of APPNAME with your Django project name. Then, create another file for HTTPS:

    touch /opt/yourpath/apache2/conf/vhosts/project-https-vhost.conf

And populate it with similar content, replacing APPNAME as appropriate.

After updating the configurations, restart the Apache server. Your Django site should now be operational.

Lastly, isolate Python dependencies within a virtual environment to avoid dependency issues and version conflicts. Inside your project directory, run:

    pip install virtualenv
    virtualenv venv
    source venv/bin/activate

This creates a folder containing all your Python executables. Subsequent pip install commands will affect only this folder. Now, go back and edit project-vhost.conf and project-https-vhost.conf, changing the python-home path to point to the venv folder:

From:

    WSGIDaemonProcess APPNAME python-home=/opt/yourpath/python python-path=/opt/yourpath/projects/APPNAME

To:

    WSGIDaemonProcess APPNAME python-home=/opt/yourpath/projects/APPNAME/venv python-path=/opt/yourpath/projects/APPNAME

Be sure to point the Python home path to the venv folder, not to the /bin executable or the Python location, to avoid a 500 error. If you encounter issues, check the Apache server error log:

    tail /opt/yourpath/apache2/logs/error_log

That's it! Navigate to your public IP address, and you should see your Django page.

P.S. If you encounter a timeout error at the WSGI level:

    Timeout when reading response headers from daemon process

Edit project-vhost.conf and project-https-vhost.conf to add the following line below WSGIDaemonProcess:

    WSGIApplicationGroup %{GLOBAL}

This addition can resolve timeouts caused by Python C extension modules, such as NumPy.

Set Up a Django Server with Apache Virtual Host and Python Virtual Environment

Hello and welcome back to another episode of Continuous Improvement, the podcast where we explore tips, tricks, and strategies to help you improve your skills and workflows. I'm your host, Victor, and today we're going to dive into a step-by-step guide on setting up a Django application for production.

But before we begin, a quick reminder to subscribe to our podcast and follow us on social media to stay updated with the latest episodes. Alright, let's jump right in!

Setting up a Django application for production can be a bit daunting, but don't worry, I've got you covered. I've broken down the process into simple steps to make it easier for you to follow along. So let's get started.

Step one, assuming you already have your CentOS or Ubuntu instance running and Python installed, create a folder for your project and set the appropriate permissions. You can do this by running the following commands:

sudo mkdir /opt/yourpath/projects
sudo chown $USER /opt/yourpath/projects

Step two, if you haven't already initialized your project, you can do so by installing Django and starting your project with the following command:

python -m pip install Django
django-admin startproject APPNAME /opt/yourpath/projects/APPNAME

Remember to replace 'APPNAME' with your desired project name.

Moving on to step three, by default, the Django development server runs on port 8000. You can start the server with the command:

python manage.py runserver

Great! Now that we have our project set up, let's prepare it for production. Step four involves editing the 'settings.py' file with a few configurations. Set 'DEBUG' to False, 'ALLOWED_HOSTS' to ['*'], 'STATIC_URL' to '/static/' and 'STATIC_ROOT' to the appropriate directory path.

After making these changes, it's time for step five. We need to collect and build the static files of our Django project. Run the following command in your project's directory:

python manage.py collectstatic --noinput

The next step, step six, is to serve your web application via the Apache web server. Assuming you have Apache2 installed, enable virtual hosts for your project and create a virtual host configuration file. You can create the file by running:

touch /opt/yourpath/apache2/conf/vhosts/project-vhost.conf

In this file, you'll need to specify the WSGIDaemonProcess for your Django application. Make sure to replace all instances of 'APPNAME' with your actual project name.

To enable HTTPS, you can create another virtual host configuration file with a similar content structure. Use the command:

touch /opt/yourpath/apache2/conf/vhosts/project-https-vhost.conf

Now that we've updated the configurations, it's time for step seven - restarting the Apache server. This will allow your Django site to become operational.

Lastly, in step eight, we'll isolate Python dependencies within a virtual environment to avoid any version conflicts or dependency issues. Inside your project directory, run the following commands:

pip install virtualenv
virtualenv venv
source venv/bin/activate

This creates a folder named 'venv' that contains all your Python executables. Any subsequent 'pip install' commands will affect only this folder.

Once you've activated the virtual environment, go back and edit 'project-vhost.conf' and 'project-https-vhost.conf'. Update the 'python-home' path in both files to point to the 'venv' folder, like this:

WSGIDaemonProcess APPNAME python-home=/opt/yourpath/projects/APPNAME/venv python-path=/opt/yourpath/projects/APPNAME

Make sure to replace 'APPNAME' with your project name.

And that's it! You've successfully set up your Django application for production. Now, all that's left is to navigate to your public IP address and you should see your Django page up and running.

If you encounter any issues along the way, remember to check the Apache server error log for troubleshooting. You can do this by running:

tail /opt/yourpath/apache2/logs/error_log

That wraps up today's episode on setting up a Django application for production. I hope you found this guide helpful and that it saves you time and effort in the future.

Remember, continuous improvement is all about learning, growing, and finding better ways to do things. If you have any questions or topics you'd like me to cover in future episodes, feel free to reach out to me on social media. Thank you for tuning in to Continuous Improvement, and until next time, keep improving!

使用Apache虛擬主機和Python虛擬環境設置Django服務器

花了我一些時間才使所有東西都能一起工作,所以我想把步驟記錄下來,以節省你未來的時間。

首先,假設你已經有你的CentOS/Ubuntu實例正在運行,並且已安裝Python。創建一個專案資料夾並設定適當的權限:

    sudo mkdir /opt/yourpath/projects
    sudo chown $USER /opt/yourpath/projects

如果你還沒有初始化你的專案,你可以這樣做:

    python -m pip install Django
    django-admin startproject APPNAME /opt/yourpath/projects/APPNAME

默認情況下,服務器運行在8000端口:

    python manage.py runserver

為了準備你的Django服務器進入生產模式,編輯settings.py文件,使用以下設置:

    DEBUG = False
    ALLOWED_HOSTS = ['*']
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')

然後,你可以使用下面的命令構建靜態文件:

    python manage.py collectstatic --noinput

接下來,通過Apache網絡服務器來提供你的web應用程式。假設你透過yum或apt-get安裝了Apache2,為你的專案啟用虛擬主機,並創建以下文件:

    touch /opt/yourpath/apache2/conf/vhosts/project-vhost.conf

在下面填入内容:

    <IfDefine !IS_APPNAME_LOADED>
      Define IS_APPNAME_LOADED
      WSGIDaemonProcess APPNAME python-home=/opt/yourpath/python python-path=/opt/yourpath/projects/APPNAME
    </IfDefine>
    <VirtualHost 127.0.0.1:80 _default_:80>
    ...

記住要用你的Django專案名稱替換所有的APPNAME。然後,為HTTPS創建另一個文件:

    touch /opt/yourpath/apache2/conf/vhosts/project-https-vhost.conf

並用相似的內容填充它,適當替換APPNAME。

更新配置後,重新啟動Apache服務器。你的Django站點現在應該已經可以運行了。

最後,在虛擬環境內隔離Python依賴,以避免依賴問題和版本衝突。在你的專案目錄裡,運行:

    pip install virtualenv
    virtualenv venv
    source venv/bin/activate

這會創建一個包含所有Python可執行文件的資料夾。後續的pip install命令只會影響這個資料夾。現在,回去編輯project-vhost.confproject-https-vhost.conf,將 python-home路徑改為指向venv資料夾:

由:

    WSGIDaemonProcess APPNAME python-home=/opt/yourpath/python python-path=/opt/yourpath/projects/APPNAME

變為:

    WSGIDaemonProcess APPNAME python-home=/opt/yourpath/projects/APPNAME/venv python-path=/opt/yourpath/projects/APPNAME

一定要將Python home路徑指向venv資料夾,而不是/bin可執行檔或Python位置,以避免出現500錯誤。如果你遇到問題,請檢查Apache服務器的錯誤日誌:

    tail /opt/yourpath/apache2/logs/error_log

就這樣!在你的公開IP地址上導航,你應該能看到你的Django頁面。

附註:如果你在WSGI層面遇到超時錯誤:

    Timeout when reading response headers from daemon process

編輯project-vhost.confproject-https-vhost.conf,在WSGIDaemonProcess下面添加以下行:

    WSGIApplicationGroup %{GLOBAL}

這種增加可以解決由Python C擴展模組(如NumPy)導致的超時。

Stop Downloading Apps for Everything

Apps are becoming increasingly irrelevant. There's no need to download ineffective software when browsers can serve as adequate substitutes. The primary benefit of using apps is data gathering and ad serving, which favor tech giants rather than end-users.

A competent programmer invests time in refining their work. They construct test cases, simplify complex problems into smaller tasks, and think deeply about the subject at hand. In contrast, less skilled developers often lack the will or talent to achieve anything significant. As a user, it's difficult to determine which app is superior.

When a developer makes an error, the repercussions are usually minor. At worst, they might receive a poor rating on the app store. In more severe cases, management may hold them accountable. However, for the most part, there are no lasting consequences. Some developers may patch the flaws, while others may introduce new ones. You won't know unless you run comprehensive regression tests.

The problem isn't solely with poor developers; even talented ones find themselves trapped in an increasingly irrational industry. These skilled programmers often lack the time to specify requirements or plan thoroughly. Furthermore, they're under immense pressure to churn out new features constantly, especially since the release of the latest iPhone. Many companies expect their developers to produce multiple new features daily to boost app downloads, a pace that's unsustainable for maintaining quality.

Creating well-tested, robust software that handles all possible states is a challenge in itself. This is made even more difficult by constant interruptions from Slack messages and progress report meetings. Often, companies hire experienced full-stack engineers to complete the work, only to find that it's prohibitively expensive.

Users are largely unaware of these challenges. Burdened by spaghetti code and countless defects, software developers become disenchanted, cynical, or both. It's no wonder that many transition to project management roles, which offer less stress, higher income, and more predictable hours. They no longer aim to transform the world through better software.

The blame for this dysfunctional software engineering culture doesn't solely rest on the companies. Tech behemoths like Apple, Google, Facebook, and Amazon have shaped the way we do business. As we spend more time on their platforms, they grow more successful, encouraging a race to the bottom in terms of software quality. My advice, especially to my fellow software developers, is to opt out of this race. True professionals should pride themselves on their carefully crafted work, rather than behaving like code monkeys.

Stop Downloading Apps for Everything

Hello and welcome to "Continuous Improvement," the podcast where we explore ways to improve ourselves and the world around us. I'm your host, Victor, and in today's episode, we'll be diving into a topic that affects both developers and users alike - the growing irrelevance of mobile apps.

It's no secret that apps have become an integral part of our lives. We rely on them for various tasks, from ordering food to tracking our workouts. But have you ever stopped to think about the effectiveness of those apps?

According to a recent blog post I came across, apps are becoming increasingly irrelevant. Why download and clutter your device with ineffective software when your browser can serve as an adequate substitute? It seems the primary benefit of using apps lies in data gathering and ad serving, which favor tech giants rather than us, the end-users.

As a user, it's difficult to determine which app is superior and genuinely offers value. The post suggests that the competency of the programmer behind an app plays a significant role in its effectiveness. Skilled programmers invest time in refining their work - constructing test cases, simplifying complex problems, and thinking deeply about the subject at hand.

On the other hand, less skilled developers may lack the necessary will or talent to achieve anything significant. This leads to a sea of apps where quality is questionable at best.

So, what happens when a developer makes an error? Well, the repercussions are often minor. At worst, they might receive a poor rating on the app store, but for the most part, there are no lasting consequences. Some developers might patch the flaws, while others may introduce new ones. It's a constant cycle of trial and error, leaving us, the users, with unreliable software and little control over its quality.

But the problem doesn't solely lie with poor developers. Even talented programmers find themselves trapped in an increasingly irrational industry. They lack the time to specify requirements or plan thoroughly, and are under immense pressure to churn out new features constantly. All this to keep up with the industry's demand for constant innovation.

Creating robust, well-tested software that handles all possible states is a challenge in itself. And to top it off, developers have to deal with constant interruptions, like Slack messages and progress report meetings, which only add to their struggles. Companies often hire experienced full-stack engineers to complete the work, only to find that it's prohibitively expensive.

Users, on the other hand, are largely unaware of these challenges. They simply want reliable and efficient apps, but are often burdened with spaghetti code and countless defects. It's no wonder that many talented developers transition to project management roles, seeking less stress, higher income, and more predictable hours. The ambition to transform the world through better software wanes.

But who can we blame for this dysfunctional software engineering culture? The post argues that tech behemoths like Apple, Google, Facebook, and Amazon have shaped the industry with their platforms and policies. As we spend more time on their platforms, they become more successful, further encouraging a race to the bottom in terms of software quality.

So, what's the solution? The author advises software developers, like myself and many of you listening, to opt out of this race. True professionals should take pride in their carefully crafted work and refuse to be treated like mere code monkeys.

And that brings us to the end of today's episode of "Continuous Improvement." I hope you found this discussion thought-provoking, whether you're a developer or a user of mobile apps.

Remember, the power for change lies in the hands of those who care about the quality of software we use every day. Let's strive for better, more reliable apps that truly serve us.

Join me next time as we delve into another topic of continuous improvement. Until then, take care and keep striving for excellence.

不要為所有事情下載應用程式

應用程式越來越無關緊要。 當瀏覽器可以作為足夠的替代品時,就不需要下載無效的軟件。 使用應用程式的主要好處是數據收集和廣告服務,這有利於科技巨頭而不是終端用戶。

一個能幹的程序員會花時間淬煉他們的工作。他們構建測試案例,將複雜的問題簡化為較小的任務,並對手頭的主題進行深思熟慮。相比之下,技能較差的開發人員往往缺乏實現任何重大成就的意願或才能。作為用戶,很難確定哪個應用程式更優越。

當開發人員犯錯時,後果通常是次要的。最壞的情況下,他們可能會在應用商店上獲得差評。在更嚴重的情況下,管理層可能會追究他們的責任。但是,大多數情況下,沒有持久的後果。有些開發人員可能會修補這些缺陷,而其他人可能會引入新的缺陷。您不進行全面的回歸測試就無法知道。

問題不僅僅在於差劣的開發者;即使是有才華的人也發現自己陷入了一個日益非理性的行業。這些技能優秀的程序員往往沒有時間來明確要求或進行 gronding 的計劃。此外,他們承受著不斷推出新功能的巨大壓力,特別是自從最新的 iPhone 發布以來。許多公司希望他們的開發人員能每天產出多個新功能以增加應用程式的下載量,但這種節奏對於保持質量來說是無法持續的。

創建經過充分測試,堅固的軟件以處理所有可能的狀態本身就是一項挑戰。這更加困難,因為來自 Slack 消息和進度報告會議的持續干擾。通常,公司會雇用經驗豐富的全棧工程師來完成工作,卻發現這成本過高。

用戶大多並未意識到這些挑戰。受到 spaghetti 代碼和無數缺陷的困擾,軟件開發人員變得不滿或悲觀,甚至兩者兼而有之。難怪許多人轉行成為項目管理角色,這提供了較少的壓力,更高的收入和更可預見的工作時間。他們不再致力於通過更好的軟件改變世界。

這種失功能的軟件工程文化的責任並不完全在於公司。像 Apple、Google、Facebook 和 Amazon 這樣的科技巨頭塑造了我們做生意的方式。當我們在他們的平台上花費更多的時間,他們就會變得更成功,從而鼓勵了軟件質量的低劣競爭。我特別建議我的軟件開發同行退出這場競賽。真正的專業人士應該以他們精心打造的工作為傲,而不是表現得像寫代碼的猴子。