This article has some flaws/misunderstandings.

ACID Transaction:

They state that MySQL has transactional DDL in a table, with the caveat that it "can't be performed within another transaction," which is linked to the MySQL docs that clearly state that is it NOT transactional DDL.

Replication:

They state that Postgres is physical via WAL, MySQL is logical vs binlog. While the first part is definitely true, arguably MySQL using row-based replication is close to physical replication, in that it describes the end state for a given tuple, rather than re-executing a statement. This also elides the fact that MySQL doesn't need a unified WAL because it has undo logs and redo logs, which combined with binlogs, perform all necessary functions of the WAL.

JSON:

They state that Postgres has more operators to access JSON features, and that Postgres allows the creation of indices on JSON fields.

I would argue that MySQL is if anything less confusing, since it only has a single type (JSON) which internally stores the data in a binary format; Postgres has both JSON and JSONB, forcing you to decide - if you know the difference and their pros/cons at all.

Furthermore, MySQL has plenty of operators to access and modify JSON values, including in-place partial updates. Perhaps if the article listed what specific operators were lacking in MySQL, I could have a stronger opinion.

As to indices, MySQL allows you to create indices on generated columns from extracted scalars of a JSON value. Postgres can create GIN indices, but only if you're using JSONB. The tradeoff in complexity between the two, I think, is in the DDL vs. DML (or DQL if you're being pedantic). With MySQL, you have to add a generated column [0] (which can be made invisible, if you want to hide it from SELECT * queries) from whatever scalar you'd like to index, and then use _that_ column in WHERE predicates. Conversely, Postgres lets you create the index using nearly the same syntax as a normal index, but then forces you to use abstruse operators [1] in queries.

Window Functions:

They say that "Postgres supports more advanced window functions, such as LAG(), LEAD(), FIRST_VALUE(), and LAST_VALUE()."

MySQL supports all of those [2].

Extensibility:

Admittedly, Postgres has a larger ecosystem. The specific example they list - PostGIS - has some support natively in MySQL, though, via its spatial data types [3]. I've not used it myself, and know it lacks K-nearest-neighbor searches for now, but to imply that there is zero support for it in MySQL is untrue.

Usability:

"MySQL allows to include non-aggregated columns in a SELECT that uses the GROUP BY clause. Postgres doesn't."

By default, MySQL does not allow this (ONLY_FULL_GROUP_BY). If you desire that behavior, you can modify it for a session, or globally.

"MySQL is case-insensitive by default. Postgres is case-sensitive by default."

This isn't exactly true. Postgres has a default collation that is case-sensitive, yes, but it also silently casts unquoted identifiers to lowercase, thus `SELECT fOO FROM baR` is identical to `SELECT foo FROM bar`. If you use double-quotes, it maintains case as specified. MySQL does use a case-insensitive (and accent-insensitive) collation by default, true, and you can switch that out at a database, table, or column level as desired.

[0]: https://dev.mysql.com/doc/refman/8.0/en/create-table-seconda...

[1]: https://www.postgresql.org/docs/current/functions-json.html#...

[2]: https://dev.mysql.com/doc/refman/8.0/en/window-function-desc...

[3]: https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overvie...

[4]: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html