To create new wiki account, please join us on #znc at Libera.Chat and ask admins to create a wiki account for you. You can say thanks to spambots for this inconvenience.

Ideas

From ZNC
Revision as of 18:16, 7 March 2014 by >Jpnurmi
Jump to navigation Jump to search

This page collects random ideas for the future development of ZNC. Everyone is welcome to add their ideas on the list. Please tag each idea with your username.

Note: Feature requests should be filed at GitHub.

Disclaimer: Some of the ideas are not going to be taken seriously, some are shot down right away, and some might even cause fuss and anger. Yet, please put emotions aside and try to be constructive with your comments.

Project structure (jpnurmi)

Modularity and extensibility is admittely one of the key ingredients in ZNC's recipe for success. These days there is a ZNC module for pretty much any imaginable IRC task. Therefore modules deserve to be treated as first class citizens, with good care. ;)

Related to versioning and compatibility as discussed below; since ZNC offers no stable module API and only does major feature releases, modules have somewhat high tendency to break on every ZNC version upgrade. This in turn makes such stakeholders as Linux distributions and ZNC hosting providers resistant to upgrade to the latest version. A stable module API would not only help module developers, but also anyone hosting a ZNC service.

In order to achieve a stable module API, the ZNC core could be refactored so that the core types would be gradually moved to a separate library. It would be nice to make it a separate build unit to avoid undesired backwards dependencies to the ZNC application code.

  ___________         ___________
 /  ZNC app  \       /  ZNC lib  \        ___________
|             |     |             |      /           \
| - main()    | --> | - CModule   | <-- | ZNC modules |
| - CModules  |     | - CString   |      \___________/
| - ...       |     | - ...       |
 \___________/       \___________/
 

Notice that the library should be absolutely minimal. Most importantly, the public API should not contain anything ZNC application specific. That library would then eventually act as an interface for modules. This restructuring would greatly help maintaining a stable module API in the future, and thus make modules much less prone to break. ZNC would have full control over what modules have access to ie. modules would no longer be able to mess with ZNC internals that have no promise of API compatibility.

J-P Nurmi (talk) 13:50, 7 March 2014 (CET)


Releasing and branching model (jpnurmi)

Predictable releases and versions are important for such stakeholders as Linux packagers. The current release model, where every release is an incompatible major release, makes it a bit unattractive to update ZNC. I'd like to propose switching to a "ZNC compatible interpretation" of [semantic versioning]. It has become almost like an industry standard, and for what it's worth, it's also recommended by [GitHub]. The whole releasing process involves branching and versioning, and results to certain level of [compatibility promise] as discussed below.

Major version

A new major version (eg. 2.0.0) is introduced when it's a time for a serious overhaul (eg. fixing an architectural shortcoming). A major version _may_ introduce any sort of breaks. Any deprecated API may be removed, and the existing API might undergo major changes. A major version is also a good opportunity for a general cleanup.

What comes to obtrusive ideas that are not compatible with the current major version (eg. 2.x series), it's often a good idea to add easy to search TODO comments in relevant places in the code, write down the ideas somewhere, or use feature branches (preferably personal forks) to prepare an implementation of the feature for the next major version.

Minor version

A new minor version (eg. 2.1.0) _may_ introduce ABI (binary) incompatible changes (eg. adding new virtual module hooks). However, minor versions shall be still kept backwards _source_ compatible meaning that existing modules can be rebuilt without changes to the module source code. In principle, new types and methods may be introduced, but any existing API _must not_ be removed or changed in source incompatible way. Old types and methods may be marked as deprecated but _must_ remain functional to some degree (common sense applies here).

Patch version

A new patch version (eg. 2.0.1) aka "bug fix release" _must not_ introduce any ABI (binary) incompatible changes. This ensures that existing modules keep running without the need of rebuilding them. In practice this means that only bug fixes to the internal implementation are allowed. In principle, public API changes are a no go.

Branches

Depending on the situation, master forms the next minor or major version. New features are always pushed to master. The preparation of a major or minor release starts by branching it from master. Meanwhile, feature development of the next major or minor version continues in master. Non-obtrusive bug fixes are always pushed to the latest applicable and supported version branch. These version branches are periodically merged to the next version branches and eventually master. The following chart illustrates a branching model based on semantic versioning:

master _____2.1_________________2.2____________________________2.3/3.0__ ...
          \             ^     \                             ^
           \           /       \                           /
2.0         v__2.0.0__/__2.0.1__v__2.0.2_______...        /
                                 \         \             /
                                  \         \           /
2.1                                v__2.1.0__v__2.1.1__/__2.1.2__ ...

J-P Nurmi (talk) 16:39, 7 March 2014 (CET)


Coding style and conventions (jpnurmi)

Consistency, consistency, and consistency... ;)

Class/header naming convention

Currently, class names are C-prefixed (eg. CBuffer) and some have an additional IRC-prefix (why CIRCNetwork but CChan - is it not an IRC channel? :P). Header file names don't have the C-prefix, but some have a ZNC-prefix (eg. ZNCString.h for historical reasons to avoid name clashes). First of all, I'd like to propose getting rid of the MFC-like C-prefix (shivers). Secondly, for the library parts, I'd like to propose a one to one mapping between class names and the corresponding header file names. That way developers always know which header to include when they want to use a particular type.

Options:

  • znc namespace: znc::Buffer, znc::Channel, znc::Network, znc::String...
    • *note*: make sure that no public header has "using znc" statements
  • Qt-style static Z-prefix: ZBuffer, ZChannel, ZNetwork, ZString...
    • not that much of an improvement, besides it looks prettier than the C-prefix?
  • lower-case header files that follow the class names: (z)buffer.h, (z)channel.h, (z)network.h, (z)string.h, ...
    • string.h is asking for trouble even if it's in a znc-folder?
  • forwarding convenience headers: (Z)Buffer, (Z)Channel, (Z)Network, (Z)String...
    • no need for a .h suffix
    • does nothing but includes the actual header

Big renames are always painful. There's the initial hurdle, but the goal is to make the module interface beautiful and consistent for the greater benefit. Developers love consistency - let's make the ZNC module interface lovable. ;)


Calling convention

Pass value types by reference

Copyable value types (excluding primitive types) such as strings and containers should be passed by reference. I believe this is already done all around the ZNC code base, but I want to make a clear distinction to complex/polymorphic types.

Pass complex/polymorphic types as pointers

Most C++ books recommend using references whenever possible, according to the general perception that references are "safer and nicer" than pointers. This is exactly what ZNC is currently doing. The downside of this approach is that it leads to a hassle with taking addresses of references vs. dereferencing pointers back and forth. Such inconsistency makes module developers unsure which calling convention applies to which function. The need of looking up the documentation/header, or having to try'n'error with compiler causes frustration that could be easily avoided. Furthermore, the distinction between value types vs. complex/polymorphic types makes it easier to see at a glance that which types are copyable and which are not but have the danger of getting sliced.

J-P Nurmi (talk) 17:45, 7 March 2014 (CET)