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: Difference between revisions

From ZNC
Jump to navigation Jump to search
>Jpnurmi
No edit summary
>Jpnurmi
No edit summary
Line 84: Line 84:
** does nothing but includes the actual header
** does nothing but includes the actual header
**:Any reason for those, except "to look like Qt"? --[[User:DarthGandalf|DarthGandalf]] ([[User talk:DarthGandalf|talk]]) 21:19, 7 March 2014 (CET)
**:Any reason for those, except "to look like Qt"? --[[User:DarthGandalf|DarthGandalf]] ([[User talk:DarthGandalf|talk]]) 21:19, 7 March 2014 (CET)
**::This is definitely the least important suggestion on this particular list. :) Sometimes, for the sake of sanity, you might want to declare multiple classes in the same header. Such reasons could be inheritance or tight coupling. For example, Foo has tiny little subclasses FooBar & FooBaz that are all placed in the same header foo.h. That's where such forwarding convenience headers step in. When module developers use either FooBar or FooBaz, they can be also sure that they can include <FooBar> or <FooBaz> respectively, without having to care about or dig into implementation details. [[User:Jpnurmi|J-P Nurmi]] ([[User talk:Jpnurmi|talk]]) 00:02, 8 March 2014 (CET)


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. ;)
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. ;)

Revision as of 23:02, 7 March 2014

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)

If interface for modules is limited, modules can't do much... --DarthGandalf (talk) 21:19, 7 March 2014 (CET)
I don't wish to cripple it either, but things like CModules and CModule::SetUser/Client/Network() etc. are probably irrelevant for module developers. It's better to start with less and expose more upon demand. The proposed versioning model also allows freely adding new stuff in minor versions, as long as it doesn't completely break the existing set. :) Anyway, since you already have a nice collection of various modules, it should be easy to figure out what is actually needed and what could be "cleaned up" as in kept on the app side. J-P Nurmi (talk) 23:09, 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?
      How so? --DarthGandalf (talk) 21:19, 7 March 2014 (CET)
      Intentional name clashes with system headers just sounds questionable. I know znc-buildmod is responsible for taking care of correct include paths and making sure not add the znc subfolder so that module developers are forced to use the <znc/string.h> syntax, but still. It feels short-sighted, because who knows how libznc evolves in the future. J-P Nurmi (talk) 23:09, 7 March 2014 (CET)
  • forwarding convenience headers: (Z)Buffer, (Z)Channel, (Z)Network, (Z)String...
    • no need for a .h suffix
    • does nothing but includes the actual header
      Any reason for those, except "to look like Qt"? --DarthGandalf (talk) 21:19, 7 March 2014 (CET)
      This is definitely the least important suggestion on this particular list. :) Sometimes, for the sake of sanity, you might want to declare multiple classes in the same header. Such reasons could be inheritance or tight coupling. For example, Foo has tiny little subclasses FooBar & FooBaz that are all placed in the same header foo.h. That's where such forwarding convenience headers step in. When module developers use either FooBar or FooBaz, they can be also sure that they can include <FooBar> or <FooBaz> respectively, without having to care about or dig into implementation details. J-P Nurmi (talk) 00:02, 8 March 2014 (CET)

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.

Anything wrong with taking addresses of references? --DarthGandalf (talk) 21:19, 7 March 2014 (CET)
Nothing in particular when formed like that. The calling sequence often goes back and forth between a module and the core, right? It gets a bit clumsy when such operations are done repeatedly. It's more convenient to pass an argument further to other methods as is, without having to think about such things. J-P Nurmi (talk) 23:09, 7 March 2014 (CET)

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.

Or use some IDE which shows you the function signatures while you type (e.g. vim :P). If someone is not familiar with codebase, they will need to lookup such signatures anyway. --DarthGandalf (talk) 21:19, 7 March 2014 (CET)
Yeah, tools can help. Combine that with muscle memory and nothing stops you. Muscle memory doesn't develop well, though, if one needs to constantly pause to check the auto-completion signature or for the poor ones, to read the docs. My preference would be simply not to call methods when the argument would be null. If it can be null, it needs to be document, and that's the case already. J-P Nurmi (talk) 23:09, 7 March 2014 (CET)

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)