Fast bytecode-based implementations of dynamic languages, like JavaScriptCore, do optimizations after the bytecode is generated. The bytecode is just the common frame of reference for profiling and OSR.
LuaJIT is the same, as is V8 (at least with Ignition & TurboFan). The article's assumption that fewer instructions = faster does not necessarily apply to these implementations, but is approximately correct for CPython (because it's a simple bytecode interpreter).